AgentCtrl Cheat Sheet¶
Note: AgentCtrl::gemini() and AgentType::Gemini are deprecated. The Gemini CLI bridge is kept for compatibility only.
Entry Points¶
use Cognesy\AgentCtrl\AgentCtrl;
use Cognesy\AgentCtrl\Config\AgentCtrlConfig;
use Cognesy\AgentCtrl\Enum\AgentType;
$builder = AgentCtrl::claudeCode();
$builder = AgentCtrl::codex();
$builder = AgentCtrl::openCode();
$builder = AgentCtrl::pi();
$builder = AgentCtrl::gemini(); // deprecated
$builder = AgentCtrl::make(AgentType::Codex);
AgentType values:
AgentType::ClaudeCodeAgentType::CodexAgentType::OpenCodeAgentType::PiAgentType::Gemini(deprecated)
Backed values:
claude-codecodexopencodepigemini
Common Builder API¶
All builders support:
withConfig(AgentCtrlConfig $config): staticwithModel(string $model): staticwithTimeout(int $seconds): staticinDirectory(string $path): staticwithSandboxDriver(SandboxDriver $driver): staticonText(callable $handler): staticonToolUse(callable $handler): staticonComplete(callable $handler): staticonError(callable $handler): staticexecute(string|\Stringable $prompt): AgentResponseexecuteStreaming(string|\Stringable $prompt): AgentResponsebuild(): AgentBridge
Callback signatures:
onText(fn(string $text): void)onToolUse(fn(string $tool, array $input, ?string $output): void)onComplete(fn(AgentResponse $response): void)onError(fn(string $message, ?string $code): void)
AgentCtrlConfig shared fields:
modeltimeoutworkingDirectorysandboxDriver
AgentCtrlConfig additional methods:
fromDsn(string $dsn): selfwithOverrides(array $values): selftoArray(): array
AgentCtrlConfig::fromArray() also accepts:
directory->workingDirectorysandbox->sandboxDriver
$builder = AgentCtrl::codex()->withConfig(AgentCtrlConfig::fromArray([
'model' => 'gpt-5-codex',
'timeout' => 300,
'directory' => getcwd(),
'sandbox' => 'host',
]));
Claude Code¶
withSystemPrompt(string|\Stringable $prompt): staticappendSystemPrompt(string|\Stringable $prompt): staticwithMaxTurns(int $turns): staticwithPermissionMode(PermissionMode $mode): staticverbose(bool $enabled = true): staticcontinueSession(): staticresumeSession(string $sessionId): staticwithAdditionalDirs(array $paths): static
Codex¶
withSandbox(SandboxMode $mode): staticdisableSandbox(): staticfullAuto(bool $enabled = true): staticdangerouslyBypass(bool $enabled = true): staticskipGitRepoCheck(bool $enabled = true): staticcontinueSession(): staticresumeSession(string $sessionId): staticwithAdditionalDirs(array $paths): staticwithImages(array $imagePaths): static
OpenCode¶
withAgent(string $agentName): staticwithFiles(array $filePaths): staticcontinueSession(): staticresumeSession(string $sessionId): staticshareSession(): staticwithTitle(string $title): static
Pi¶
withProvider(string $provider): staticwithThinking(ThinkingLevel $level): staticwithSystemPrompt(string|\Stringable $prompt): staticappendSystemPrompt(string|\Stringable $prompt): staticwithTools(array $tools): staticnoTools(): staticwithFiles(array $filePaths): staticwithExtensions(array $extensions): staticnoExtensions(): staticwithSkills(array $skills): staticnoSkills(): staticwithApiKey(string $apiKey): staticcontinueSession(): staticresumeSession(string $sessionId): staticephemeral(): staticwithSessionDir(string $dir): staticverbose(bool $enabled = true): static
Gemini¶
withApprovalMode(ApprovalMode $mode): staticyolo(): staticplanMode(): staticwithSandbox(bool $enabled = true): staticwithIncludeDirectories(array $paths): staticwithExtensions(array $extensions): staticwithAllowedTools(array $tools): staticwithAllowedMcpServers(array $names): staticwithPolicy(array $paths): staticcontinueSession(): staticresumeSession(string $sessionId): staticdebug(bool $enabled = true): static
Sessions and Executions¶
use Cognesy\AgentCtrl\AgentCtrl;
$response = AgentCtrl::codex()->execute('Create a plan.');
$next = AgentCtrl::codex()
->continueSession()
->execute('Apply step 1.');
$sessionId = $response->sessionId();
if ($sessionId !== null) {
$again = AgentCtrl::codex()
->resumeSession((string) $sessionId)
->execute('Apply step 2.');
}
Identity rules:
executionId()returnsAgentCtrlExecutionIdsessionId()returnsAgentSessionId|nullexecutionId()is generated once perexecute()orexecuteStreaming()callsessionId()is provider continuity metadata for resumed conversations- separate runs may share one
sessionId()and still have differentexecutionId()values
Session correlation in telemetry¶
AgentExecutionStarted and AgentExecutionCompleted both expose sessionId(): ?AgentSessionId,
and AgentCtrlEventTelemetry puts it on the envelope correlation. When it is populated:
resumeSession($id)— the root is born with the session; this is the earliest point the runtime honestly knows one.continueSession()— the root has no session. The flag selects "the most recent" without naming it, and no placeholder is invented. Gemini is the sharp case:continueSession()writes the literal'latest'into the same fieldresumeSession()uses, and'latest'is excluded.- fresh run — no session at start; the agent reports one, so it lands on the completion event.
The root operation id is always the execution id, so runs sharing a session stay distinct traces that merely correlate.
Response¶
AgentResponse public properties:
agentTypetextexitCodeusagecosttoolCallsrawResponseparseFailures
AgentResponse methods:
executionId(): AgentCtrlExecutionIdsessionId(): ?AgentSessionIdisSuccess(): booltext(): stringusage(): ?TokenUsagecost(): ?floatparseFailures(): intparseFailureSamples(): array
Tool Calls and Usage¶
ToolCall:
toolinputoutputisErrorcallId(): ?AgentToolCallIdisCompleted(): bool
TokenUsage:
inputoutputcacheReadcacheWritereasoningtotal(): int
StreamHandler¶
StreamHandler interface (Cognesy\AgentCtrl\Contract\StreamHandler):
onText(string $text): voidonToolUse(ToolCall $toolCall): voidonComplete(AgentResponse $response): voidonError(StreamError $error): void
CallbackStreamHandler implements StreamHandler with optional closures:
hasTextHandler(): boolhasToolUseHandler(): boolhasCompleteHandler(): boolhasErrorHandler(): boolhasAnyHandler(): bool
StreamError:
message(string)code(?string)details(array<string,mixed>)
Events¶
Concrete builders also expose event wiring through HandlesEvents:
withEventHandler(CanHandleEvents $events): staticwiretap(?callable $listener): selfonEvent(string $class, ?callable $listener): self
Event classes (Cognesy\AgentCtrl\Event\*):
Execution lifecycle:
- AgentExecutionStarted -- execution begins
- AgentExecutionCompleted -- execution finishes
- ExecutionAttempted -- an execution attempt is made
Request building:
- RequestBuilt -- request object assembled
- CommandSpecCreated -- CLI command spec created
Process execution:
- ProcessExecutionStarted -- sandbox process starts
- ProcessExecutionCompleted -- sandbox process finishes
Streaming:
- StreamProcessingStarted -- stream parsing begins
- StreamChunkProcessed -- a JSONL chunk is parsed
- StreamProcessingCompleted -- stream parsing finishes
Response parsing:
- ResponseParsingStarted -- response parser begins
- ResponseDataExtracted -- data extracted from raw response
- ResponseParsingCompleted -- response parser finishes
Agent content:
- AgentTextReceived -- text content received
- AgentToolUsed -- tool call detected
- AgentErrorOccurred -- error event from agent
Sandbox:
- SandboxInitialized -- sandbox driver initialized
- SandboxPolicyConfigured -- sandbox policy set
- SandboxReady -- sandbox ready for execution
Testing¶
Deterministic test seams:
- core package logic
- test
AgentCtrlConfig, command builders, parsers, and DTOs directly Cognesy\Sandbox\Testing\FakeSandbox- use for deterministic command execution without running a real CLI
- best for bridge execution paths, timeout behavior, and stdout/stderr handling
Cognesy\Instructor\Laravel\Testing\AgentCtrlFake- use only when testing the Laravel facade layer
- queued responses and execution assertions live there, not in core
agent-ctrl
Streaming¶
use Cognesy\AgentCtrl\AgentCtrl;
use Cognesy\AgentCtrl\Dto\AgentResponse;
$response = AgentCtrl::openCode()
->onText(fn(string $text) => print($text))
->onToolUse(fn(string $tool, array $input, ?string $output) => print("[{$tool}]"))
->onComplete(fn(AgentResponse $response) => print("\nDone\n"))
->onError(fn(string $message, ?string $code) => print("\nError: {$message}\n"))
->executeStreaming('Explain this project structure.');