From 329709c62e06815b8399ea50e1c36d4c2bd13dd0 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 15 Apr 2026 19:52:28 +0000 Subject: [PATCH 01/10] Initial plan From de2a822d5f626ae10a9bbf0cc048e0f4cb105b54 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 15 Apr 2026 19:59:28 +0000 Subject: [PATCH 02/10] fix: support actions array in output computer tool call parsing Agent-Logs-Url: https://github.com/openai-php/client/sessions/18ddc838-e520-4048-879d-4a1218823f7a Co-authored-by: iBotPeaches <611784+iBotPeaches@users.noreply.github.com> --- .../Output/OutputComputerToolCall.php | 27 ++++++++++--------- .../Output/OutputComputerToolCall.php | 26 ++++++++++++++++++ 2 files changed, 41 insertions(+), 12 deletions(-) diff --git a/src/Responses/Responses/Output/OutputComputerToolCall.php b/src/Responses/Responses/Output/OutputComputerToolCall.php index cb598e53..e669e50c 100644 --- a/src/Responses/Responses/Output/OutputComputerToolCall.php +++ b/src/Responses/Responses/Output/OutputComputerToolCall.php @@ -30,7 +30,8 @@ * @phpstan-import-type WaitType from Wait * @phpstan-import-type PendingSafetyCheckType from OutputComputerPendingSafetyCheck * - * @phpstan-type OutputComputerToolCallType array{action: ClickType|DoubleClickType|DragType|KeyPressType|MoveType|ScreenshotType|ScrollType|TypeType|WaitType, call_id: string, id: string, pending_safety_checks: array, status: 'in_progress'|'completed'|'incomplete', type: 'computer_call'} + * @phpstan-type ActionType ClickType|DoubleClickType|DragType|KeyPressType|MoveType|ScreenshotType|ScrollType|TypeType|WaitType + * @phpstan-type OutputComputerToolCallType array{action?: ActionType, actions?: array, call_id: string, id: string, pending_safety_checks?: array, status: 'in_progress'|'completed'|'incomplete', type: 'computer_call'} * * @implements ResponseContract */ @@ -62,21 +63,23 @@ private function __construct( */ public static function from(array $attributes): self { - $action = match ($attributes['action']['type']) { - 'click' => Click::from($attributes['action']), - 'double_click' => DoubleClick::from($attributes['action']), - 'drag' => Drag::from($attributes['action']), - 'keypress' => KeyPress::from($attributes['action']), - 'move' => Move::from($attributes['action']), - 'screenshot' => Screenshot::from($attributes['action']), - 'scroll' => Scroll::from($attributes['action']), - 'type' => Type::from($attributes['action']), - 'wait' => Wait::from($attributes['action']), + $actionAttributes = $attributes['action'] ?? ($attributes['actions'][0] ?? []); + + $action = match ($actionAttributes['type']) { + 'click' => Click::from($actionAttributes), + 'double_click' => DoubleClick::from($actionAttributes), + 'drag' => Drag::from($actionAttributes), + 'keypress' => KeyPress::from($actionAttributes), + 'move' => Move::from($actionAttributes), + 'screenshot' => Screenshot::from($actionAttributes), + 'scroll' => Scroll::from($actionAttributes), + 'type' => Type::from($actionAttributes), + 'wait' => Wait::from($actionAttributes), }; $pendingSafetyChecks = array_map( fn (array $safetyCheck): OutputComputerPendingSafetyCheck => OutputComputerPendingSafetyCheck::from($safetyCheck), - $attributes['pending_safety_checks'] + $attributes['pending_safety_checks'] ?? [] ); return new self( diff --git a/tests/Responses/Responses/Output/OutputComputerToolCall.php b/tests/Responses/Responses/Output/OutputComputerToolCall.php index d4b6ba94..035df507 100644 --- a/tests/Responses/Responses/Output/OutputComputerToolCall.php +++ b/tests/Responses/Responses/Output/OutputComputerToolCall.php @@ -1,6 +1,7 @@ toBeArray() ->toBe(outputComputerToolCall()); }); + +test('from with actions and without pending safety checks', function () { + $payload = outputComputerToolCall(); + unset($payload['action'], $payload['pending_safety_checks']); + $payload['actions'] = [ + ['type' => 'screenshot'], + ]; + + $response = OutputComputerToolCall::from($payload); + + expect($response) + ->toBeInstanceOf(OutputComputerToolCall::class) + ->action->toBeInstanceOf(OutputComputerActionScreenshot::class) + ->pendingSafetyChecks->toBeArray()->toHaveCount(0) + ->status->toBe('completed') + ->type->toBe('computer_call'); + + expect($response->toArray()) + ->toBeArray() + ->toMatchArray([ + 'action' => ['type' => 'screenshot'], + 'pending_safety_checks' => [], + ]) + ->not->toHaveKey('actions'); +}); From 163c85549063c560aa347afb3671ee2a0c4c7d7d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 15 Apr 2026 20:01:45 +0000 Subject: [PATCH 03/10] test: cover actions fallback and missing action validation Agent-Logs-Url: https://github.com/openai-php/client/sessions/18ddc838-e520-4048-879d-4a1218823f7a Co-authored-by: iBotPeaches <611784+iBotPeaches@users.noreply.github.com> --- .../Output/OutputComputerToolCall.php | 11 ++++- .../Output/OutputComputerToolCall.php | 42 +++++++++++++++++++ 2 files changed, 52 insertions(+), 1 deletion(-) diff --git a/src/Responses/Responses/Output/OutputComputerToolCall.php b/src/Responses/Responses/Output/OutputComputerToolCall.php index e669e50c..c4cad27b 100644 --- a/src/Responses/Responses/Output/OutputComputerToolCall.php +++ b/src/Responses/Responses/Output/OutputComputerToolCall.php @@ -63,7 +63,16 @@ private function __construct( */ public static function from(array $attributes): self { - $actionAttributes = $attributes['action'] ?? ($attributes['actions'][0] ?? []); + $actionAttributes = []; + if (isset($attributes['action'])) { + $actionAttributes = $attributes['action']; + } elseif (isset($attributes['actions']) && isset($attributes['actions'][0])) { + $actionAttributes = $attributes['actions'][0]; + } + + if (! isset($actionAttributes['type'])) { + throw new \InvalidArgumentException('Missing required computer action payload.'); + } $action = match ($actionAttributes['type']) { 'click' => Click::from($actionAttributes), diff --git a/tests/Responses/Responses/Output/OutputComputerToolCall.php b/tests/Responses/Responses/Output/OutputComputerToolCall.php index 035df507..5fa475c3 100644 --- a/tests/Responses/Responses/Output/OutputComputerToolCall.php +++ b/tests/Responses/Responses/Output/OutputComputerToolCall.php @@ -54,3 +54,45 @@ ]) ->not->toHaveKey('actions'); }); + +test('from with multiple actions uses the first action', function () { + $payload = outputComputerToolCall(); + unset($payload['action']); + $payload['actions'] = [ + ['type' => 'screenshot'], + ['type' => 'wait'], + ]; + + $response = OutputComputerToolCall::from($payload); + + expect($response->action) + ->toBeInstanceOf(OutputComputerActionScreenshot::class); +}); + +test('from without action payload throws exception', function () { + $payload = outputComputerToolCall(); + unset($payload['action']); + $payload['actions'] = []; + + expect(fn (): OutputComputerToolCall => OutputComputerToolCall::from($payload)) + ->toThrow(InvalidArgumentException::class, 'Missing required computer action payload.'); +}); + +test('from without action and actions keys throws exception', function () { + $payload = outputComputerToolCall(); + unset($payload['action'], $payload['actions']); + + expect(fn (): OutputComputerToolCall => OutputComputerToolCall::from($payload)) + ->toThrow(InvalidArgumentException::class, 'Missing required computer action payload.'); +}); + +test('from with malformed actions payload throws exception', function () { + $payload = outputComputerToolCall(); + unset($payload['action']); + $payload['actions'] = [ + ['x' => 1], + ]; + + expect(fn (): OutputComputerToolCall => OutputComputerToolCall::from($payload)) + ->toThrow(InvalidArgumentException::class, 'Missing required computer action payload.'); +}); From 81c5a873a49bce3dafffeee996985adff350f583 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 19 Apr 2026 11:03:33 +0000 Subject: [PATCH 04/10] refactor: expose computer call actions as array Agent-Logs-Url: https://github.com/openai-php/client/sessions/d9e38e52-5589-4ffb-9b6e-f0eab6a45a27 Co-authored-by: iBotPeaches <611784+iBotPeaches@users.noreply.github.com> --- .../Output/OutputComputerToolCall.php | 46 +++++++++++-------- tests/Fixtures/Responses.php | 12 +++-- .../Output/OutputComputerToolCall.php | 29 ++++++++---- 3 files changed, 53 insertions(+), 34 deletions(-) diff --git a/src/Responses/Responses/Output/OutputComputerToolCall.php b/src/Responses/Responses/Output/OutputComputerToolCall.php index c4cad27b..b206945c 100644 --- a/src/Responses/Responses/Output/OutputComputerToolCall.php +++ b/src/Responses/Responses/Output/OutputComputerToolCall.php @@ -45,12 +45,13 @@ final class OutputComputerToolCall implements ResponseContract use Fakeable; /** + * @param array $actions * @param array $pendingSafetyChecks * @param 'in_progress'|'completed'|'incomplete' $status * @param 'computer_call' $type */ private function __construct( - public readonly Click|DoubleClick|Drag|KeyPress|Move|Screenshot|Scroll|Type|Wait $action, + public readonly array $actions, public readonly string $callId, public readonly string $id, public readonly array $pendingSafetyChecks, @@ -64,27 +65,31 @@ private function __construct( public static function from(array $attributes): self { $actionAttributes = []; - if (isset($attributes['action'])) { - $actionAttributes = $attributes['action']; - } elseif (isset($attributes['actions']) && isset($attributes['actions'][0])) { - $actionAttributes = $attributes['actions'][0]; + if (isset($attributes['actions'])) { + $actionAttributes = $attributes['actions']; + } elseif (isset($attributes['action'])) { + $actionAttributes = [$attributes['action']]; } - if (! isset($actionAttributes['type'])) { + if ($actionAttributes === []) { throw new \InvalidArgumentException('Missing required computer action payload.'); } - $action = match ($actionAttributes['type']) { - 'click' => Click::from($actionAttributes), - 'double_click' => DoubleClick::from($actionAttributes), - 'drag' => Drag::from($actionAttributes), - 'keypress' => KeyPress::from($actionAttributes), - 'move' => Move::from($actionAttributes), - 'screenshot' => Screenshot::from($actionAttributes), - 'scroll' => Scroll::from($actionAttributes), - 'type' => Type::from($actionAttributes), - 'wait' => Wait::from($actionAttributes), - }; + $actions = array_map( + fn (array $action): Click|DoubleClick|Drag|KeyPress|Move|Screenshot|Scroll|Type|Wait => match ($action['type'] ?? null) { + 'click' => Click::from($action), + 'double_click' => DoubleClick::from($action), + 'drag' => Drag::from($action), + 'keypress' => KeyPress::from($action), + 'move' => Move::from($action), + 'screenshot' => Screenshot::from($action), + 'scroll' => Scroll::from($action), + 'type' => Type::from($action), + 'wait' => Wait::from($action), + default => throw new \InvalidArgumentException('Missing required computer action payload.'), + }, + $actionAttributes + ); $pendingSafetyChecks = array_map( fn (array $safetyCheck): OutputComputerPendingSafetyCheck => OutputComputerPendingSafetyCheck::from($safetyCheck), @@ -92,7 +97,7 @@ public static function from(array $attributes): self ); return new self( - action: $action, + actions: $actions, callId: $attributes['call_id'], id: $attributes['id'], pendingSafetyChecks: $pendingSafetyChecks, @@ -110,7 +115,10 @@ public function toArray(): array 'type' => $this->type, 'call_id' => $this->callId, 'id' => $this->id, - 'action' => $this->action->toArray(), + 'actions' => array_map( + fn (Click|DoubleClick|Drag|KeyPress|Move|Screenshot|Scroll|Type|Wait $action): array => $action->toArray(), + $this->actions, + ), 'pending_safety_checks' => array_map( fn (OutputComputerPendingSafetyCheck $safetyCheck): array => $safetyCheck->toArray(), $this->pendingSafetyChecks, diff --git a/tests/Fixtures/Responses.php b/tests/Fixtures/Responses.php index cdafd18c..4f339087 100644 --- a/tests/Fixtures/Responses.php +++ b/tests/Fixtures/Responses.php @@ -477,11 +477,13 @@ function outputComputerToolCall(): array 'type' => 'computer_call', 'call_id' => 'call_67ccf18f64008190a39b619f4c8455ef087bb177ab789d5c', 'id' => 'cu_67ccf18f64008190a39b619f4c8455ef087bb177ab789d5c', - 'action' => [ - 'button' => 'left', - 'type' => 'click', - 'x' => 117, - 'y' => 123, + 'actions' => [ + [ + 'button' => 'left', + 'type' => 'click', + 'x' => 117, + 'y' => 123, + ], ], 'pending_safety_checks' => [ [ diff --git a/tests/Responses/Responses/Output/OutputComputerToolCall.php b/tests/Responses/Responses/Output/OutputComputerToolCall.php index 5fa475c3..31dc589d 100644 --- a/tests/Responses/Responses/Output/OutputComputerToolCall.php +++ b/tests/Responses/Responses/Output/OutputComputerToolCall.php @@ -2,6 +2,7 @@ use OpenAI\Responses\Responses\Output\ComputerAction\OutputComputerActionClick; use OpenAI\Responses\Responses\Output\ComputerAction\OutputComputerActionScreenshot; +use OpenAI\Responses\Responses\Output\ComputerAction\OutputComputerActionWait; use OpenAI\Responses\Responses\Output\OutputComputerToolCall; test('from', function () { @@ -9,7 +10,12 @@ expect($response) ->toBeInstanceOf(OutputComputerToolCall::class) - ->action->toBeInstanceOf(OutputComputerActionClick::class) + ->actions->toBeArray()->toHaveCount(1); + + expect($response->actions[0]) + ->toBeInstanceOf(OutputComputerActionClick::class); + + expect($response) ->callId->toBe('call_67ccf18f64008190a39b619f4c8455ef087bb177ab789d5c') ->id->toBe('cu_67ccf18f64008190a39b619f4c8455ef087bb177ab789d5c') ->status->toBe('completed') @@ -32,7 +38,7 @@ test('from with actions and without pending safety checks', function () { $payload = outputComputerToolCall(); - unset($payload['action'], $payload['pending_safety_checks']); + unset($payload['pending_safety_checks']); $payload['actions'] = [ ['type' => 'screenshot'], ]; @@ -41,23 +47,23 @@ expect($response) ->toBeInstanceOf(OutputComputerToolCall::class) - ->action->toBeInstanceOf(OutputComputerActionScreenshot::class) + ->actions->toBeArray()->toHaveCount(1) ->pendingSafetyChecks->toBeArray()->toHaveCount(0) ->status->toBe('completed') ->type->toBe('computer_call'); + expect($response->actions[0])->toBeInstanceOf(OutputComputerActionScreenshot::class); + expect($response->toArray()) ->toBeArray() ->toMatchArray([ - 'action' => ['type' => 'screenshot'], + 'actions' => [['type' => 'screenshot']], 'pending_safety_checks' => [], - ]) - ->not->toHaveKey('actions'); + ]); }); -test('from with multiple actions uses the first action', function () { +test('from with multiple actions maps all actions', function () { $payload = outputComputerToolCall(); - unset($payload['action']); $payload['actions'] = [ ['type' => 'screenshot'], ['type' => 'wait'], @@ -65,8 +71,11 @@ $response = OutputComputerToolCall::from($payload); - expect($response->action) - ->toBeInstanceOf(OutputComputerActionScreenshot::class); + expect($response->actions) + ->toHaveCount(2); + + expect($response->actions[0])->toBeInstanceOf(OutputComputerActionScreenshot::class); + expect($response->actions[1])->toBeInstanceOf(OutputComputerActionWait::class); }); test('from without action payload throws exception', function () { From a66b2b9eb436feeaca10b61609c92be077949682 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 19 Apr 2026 11:05:41 +0000 Subject: [PATCH 05/10] fix: model computer call actions as array Agent-Logs-Url: https://github.com/openai-php/client/sessions/d9e38e52-5589-4ffb-9b6e-f0eab6a45a27 Co-authored-by: iBotPeaches <611784+iBotPeaches@users.noreply.github.com> --- .../Responses/Output/OutputComputerToolCall.php | 4 ++-- .../Responses/Output/OutputComputerToolCall.php | 15 ++++++++++----- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/Responses/Responses/Output/OutputComputerToolCall.php b/src/Responses/Responses/Output/OutputComputerToolCall.php index b206945c..a2bb1ebc 100644 --- a/src/Responses/Responses/Output/OutputComputerToolCall.php +++ b/src/Responses/Responses/Output/OutputComputerToolCall.php @@ -72,7 +72,7 @@ public static function from(array $attributes): self } if ($actionAttributes === []) { - throw new \InvalidArgumentException('Missing required computer action payload.'); + throw new \InvalidArgumentException('No computer actions provided in payload.'); } $actions = array_map( @@ -86,7 +86,7 @@ public static function from(array $attributes): self 'scroll' => Scroll::from($action), 'type' => Type::from($action), 'wait' => Wait::from($action), - default => throw new \InvalidArgumentException('Missing required computer action payload.'), + default => throw new \InvalidArgumentException('Invalid or missing action type in computer action payload.'), }, $actionAttributes ); diff --git a/tests/Responses/Responses/Output/OutputComputerToolCall.php b/tests/Responses/Responses/Output/OutputComputerToolCall.php index 31dc589d..5171cdbf 100644 --- a/tests/Responses/Responses/Output/OutputComputerToolCall.php +++ b/tests/Responses/Responses/Output/OutputComputerToolCall.php @@ -65,7 +65,12 @@ test('from with multiple actions maps all actions', function () { $payload = outputComputerToolCall(); $payload['actions'] = [ - ['type' => 'screenshot'], + [ + 'button' => 'left', + 'type' => 'click', + 'x' => 117, + 'y' => 123, + ], ['type' => 'wait'], ]; @@ -74,7 +79,7 @@ expect($response->actions) ->toHaveCount(2); - expect($response->actions[0])->toBeInstanceOf(OutputComputerActionScreenshot::class); + expect($response->actions[0])->toBeInstanceOf(OutputComputerActionClick::class); expect($response->actions[1])->toBeInstanceOf(OutputComputerActionWait::class); }); @@ -84,7 +89,7 @@ $payload['actions'] = []; expect(fn (): OutputComputerToolCall => OutputComputerToolCall::from($payload)) - ->toThrow(InvalidArgumentException::class, 'Missing required computer action payload.'); + ->toThrow(InvalidArgumentException::class, 'No computer actions provided in payload.'); }); test('from without action and actions keys throws exception', function () { @@ -92,7 +97,7 @@ unset($payload['action'], $payload['actions']); expect(fn (): OutputComputerToolCall => OutputComputerToolCall::from($payload)) - ->toThrow(InvalidArgumentException::class, 'Missing required computer action payload.'); + ->toThrow(InvalidArgumentException::class, 'No computer actions provided in payload.'); }); test('from with malformed actions payload throws exception', function () { @@ -103,5 +108,5 @@ ]; expect(fn (): OutputComputerToolCall => OutputComputerToolCall::from($payload)) - ->toThrow(InvalidArgumentException::class, 'Missing required computer action payload.'); + ->toThrow(InvalidArgumentException::class, 'Invalid or missing action type in computer action payload.'); }); From af1220e2eae85183fc3c758bb0a533ffbb5517d4 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 19 Apr 2026 11:19:55 +0000 Subject: [PATCH 06/10] fix: allow empty computer actions payload Agent-Logs-Url: https://github.com/openai-php/client/sessions/92f1ec3d-14f5-4808-9a86-930d7f554d67 Co-authored-by: iBotPeaches <611784+iBotPeaches@users.noreply.github.com> --- .../Output/OutputComputerToolCall.php | 36 ++++++++++--------- .../Output/OutputComputerToolCall.php | 20 ++++++----- 2 files changed, 31 insertions(+), 25 deletions(-) diff --git a/src/Responses/Responses/Output/OutputComputerToolCall.php b/src/Responses/Responses/Output/OutputComputerToolCall.php index a2bb1ebc..7b8cd4c4 100644 --- a/src/Responses/Responses/Output/OutputComputerToolCall.php +++ b/src/Responses/Responses/Output/OutputComputerToolCall.php @@ -71,23 +71,8 @@ public static function from(array $attributes): self $actionAttributes = [$attributes['action']]; } - if ($actionAttributes === []) { - throw new \InvalidArgumentException('No computer actions provided in payload.'); - } - $actions = array_map( - fn (array $action): Click|DoubleClick|Drag|KeyPress|Move|Screenshot|Scroll|Type|Wait => match ($action['type'] ?? null) { - 'click' => Click::from($action), - 'double_click' => DoubleClick::from($action), - 'drag' => Drag::from($action), - 'keypress' => KeyPress::from($action), - 'move' => Move::from($action), - 'screenshot' => Screenshot::from($action), - 'scroll' => Scroll::from($action), - 'type' => Type::from($action), - 'wait' => Wait::from($action), - default => throw new \InvalidArgumentException('Invalid or missing action type in computer action payload.'), - }, + static fn (array $action): Click|DoubleClick|Drag|KeyPress|Move|Screenshot|Scroll|Type|Wait => self::mapAction($action), $actionAttributes ); @@ -126,4 +111,23 @@ public function toArray(): array 'status' => $this->status, ]; } + + /** + * @param array $action + */ + private static function mapAction(array $action): Click|DoubleClick|Drag|KeyPress|Move|Screenshot|Scroll|Type|Wait + { + return match ($action['type'] ?? null) { + 'click' => Click::from($action), + 'double_click' => DoubleClick::from($action), + 'drag' => Drag::from($action), + 'keypress' => KeyPress::from($action), + 'move' => Move::from($action), + 'screenshot' => Screenshot::from($action), + 'scroll' => Scroll::from($action), + 'type' => Type::from($action), + 'wait' => Wait::from($action), + default => throw new \InvalidArgumentException('Invalid or missing action type in computer action payload.'), + }; + } } diff --git a/tests/Responses/Responses/Output/OutputComputerToolCall.php b/tests/Responses/Responses/Output/OutputComputerToolCall.php index 5171cdbf..eb023932 100644 --- a/tests/Responses/Responses/Output/OutputComputerToolCall.php +++ b/tests/Responses/Responses/Output/OutputComputerToolCall.php @@ -83,26 +83,28 @@ expect($response->actions[1])->toBeInstanceOf(OutputComputerActionWait::class); }); -test('from without action payload throws exception', function () { +test('from with empty actions returns empty actions array', function () { $payload = outputComputerToolCall(); - unset($payload['action']); $payload['actions'] = []; - expect(fn (): OutputComputerToolCall => OutputComputerToolCall::from($payload)) - ->toThrow(InvalidArgumentException::class, 'No computer actions provided in payload.'); + $response = OutputComputerToolCall::from($payload); + + expect($response->actions)->toBeArray()->toHaveCount(0); + expect($response->toArray()['actions'])->toBeArray()->toHaveCount(0); }); -test('from without action and actions keys throws exception', function () { +test('from without action and actions keys returns empty actions array', function () { $payload = outputComputerToolCall(); - unset($payload['action'], $payload['actions']); + unset($payload['actions']); - expect(fn (): OutputComputerToolCall => OutputComputerToolCall::from($payload)) - ->toThrow(InvalidArgumentException::class, 'No computer actions provided in payload.'); + $response = OutputComputerToolCall::from($payload); + + expect($response->actions)->toBeArray()->toHaveCount(0); + expect($response->toArray()['actions'])->toBeArray()->toHaveCount(0); }); test('from with malformed actions payload throws exception', function () { $payload = outputComputerToolCall(); - unset($payload['action']); $payload['actions'] = [ ['x' => 1], ]; From 4bec82a02afb6eb7d80eae6a9fcab8c3b48be279 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 19 Apr 2026 11:22:08 +0000 Subject: [PATCH 07/10] fix: default computer actions to empty array Agent-Logs-Url: https://github.com/openai-php/client/sessions/92f1ec3d-14f5-4808-9a86-930d7f554d67 Co-authored-by: iBotPeaches <611784+iBotPeaches@users.noreply.github.com> --- .../Responses/Output/OutputComputerToolCall.php | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/tests/Responses/Responses/Output/OutputComputerToolCall.php b/tests/Responses/Responses/Output/OutputComputerToolCall.php index eb023932..c36cb6ea 100644 --- a/tests/Responses/Responses/Output/OutputComputerToolCall.php +++ b/tests/Responses/Responses/Output/OutputComputerToolCall.php @@ -95,7 +95,7 @@ test('from without action and actions keys returns empty actions array', function () { $payload = outputComputerToolCall(); - unset($payload['actions']); + unset($payload['action'], $payload['actions']); $response = OutputComputerToolCall::from($payload); @@ -112,3 +112,12 @@ expect(fn (): OutputComputerToolCall => OutputComputerToolCall::from($payload)) ->toThrow(InvalidArgumentException::class, 'Invalid or missing action type in computer action payload.'); }); + +test('from with malformed legacy action payload throws exception', function () { + $payload = outputComputerToolCall(); + unset($payload['actions']); + $payload['action'] = ['x' => 1]; + + expect(fn (): OutputComputerToolCall => OutputComputerToolCall::from($payload)) + ->toThrow(InvalidArgumentException::class, 'Invalid or missing action type in computer action payload.'); +}); From 49c6c7cdc98dd862536adb2eccf7e0fc9948894e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 19 Apr 2026 11:56:22 +0000 Subject: [PATCH 08/10] fix: satisfy phpstan types for computer action mapping Agent-Logs-Url: https://github.com/openai-php/client/sessions/17df9226-8c92-499d-999d-8ba247d09a12 Co-authored-by: iBotPeaches <611784+iBotPeaches@users.noreply.github.com> --- src/Responses/Responses/Output/OutputComputerToolCall.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Responses/Responses/Output/OutputComputerToolCall.php b/src/Responses/Responses/Output/OutputComputerToolCall.php index 7b8cd4c4..0111c22f 100644 --- a/src/Responses/Responses/Output/OutputComputerToolCall.php +++ b/src/Responses/Responses/Output/OutputComputerToolCall.php @@ -64,6 +64,7 @@ private function __construct( */ public static function from(array $attributes): self { + /** @var array $actionAttributes */ $actionAttributes = []; if (isset($attributes['actions'])) { $actionAttributes = $attributes['actions']; @@ -113,7 +114,7 @@ public function toArray(): array } /** - * @param array $action + * @param ActionType $action */ private static function mapAction(array $action): Click|DoubleClick|Drag|KeyPress|Move|Screenshot|Scroll|Type|Wait { From 08ac7b26c3f1e70272ed85284130dc55320fa29f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 19 Apr 2026 12:48:44 +0000 Subject: [PATCH 09/10] fix: avoid phpstan unreachable check in computer action match Agent-Logs-Url: https://github.com/openai-php/client/sessions/cbf38960-5239-47a6-bbd5-f926ace70703 Co-authored-by: iBotPeaches <611784+iBotPeaches@users.noreply.github.com> --- src/Responses/Responses/Output/OutputComputerToolCall.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Responses/Responses/Output/OutputComputerToolCall.php b/src/Responses/Responses/Output/OutputComputerToolCall.php index 0111c22f..f4669735 100644 --- a/src/Responses/Responses/Output/OutputComputerToolCall.php +++ b/src/Responses/Responses/Output/OutputComputerToolCall.php @@ -118,7 +118,10 @@ public function toArray(): array */ private static function mapAction(array $action): Click|DoubleClick|Drag|KeyPress|Move|Screenshot|Scroll|Type|Wait { - return match ($action['type'] ?? null) { + /** @var array $untypedAction */ + $untypedAction = $action; + + return match ($untypedAction['type'] ?? null) { 'click' => Click::from($action), 'double_click' => DoubleClick::from($action), 'drag' => Drag::from($action), From 57e862e4cd40a633528223372e574e1d741c2ccf Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 19 Apr 2026 20:34:49 +0000 Subject: [PATCH 10/10] fix: narrow computer action types per switch branch Agent-Logs-Url: https://github.com/openai-php/client/sessions/ea4389b8-7cdb-4060-85f4-45574148364e Co-authored-by: iBotPeaches <611784+iBotPeaches@users.noreply.github.com> --- .../Output/OutputComputerToolCall.php | 48 ++++++++++++------- 1 file changed, 32 insertions(+), 16 deletions(-) diff --git a/src/Responses/Responses/Output/OutputComputerToolCall.php b/src/Responses/Responses/Output/OutputComputerToolCall.php index f4669735..6c5733cd 100644 --- a/src/Responses/Responses/Output/OutputComputerToolCall.php +++ b/src/Responses/Responses/Output/OutputComputerToolCall.php @@ -114,24 +114,40 @@ public function toArray(): array } /** - * @param ActionType $action + * @param array $action */ private static function mapAction(array $action): Click|DoubleClick|Drag|KeyPress|Move|Screenshot|Scroll|Type|Wait { - /** @var array $untypedAction */ - $untypedAction = $action; - - return match ($untypedAction['type'] ?? null) { - 'click' => Click::from($action), - 'double_click' => DoubleClick::from($action), - 'drag' => Drag::from($action), - 'keypress' => KeyPress::from($action), - 'move' => Move::from($action), - 'screenshot' => Screenshot::from($action), - 'scroll' => Scroll::from($action), - 'type' => Type::from($action), - 'wait' => Wait::from($action), - default => throw new \InvalidArgumentException('Invalid or missing action type in computer action payload.'), - }; + switch ($action['type'] ?? null) { + case 'click': + /** @var ClickType $action */ + return Click::from($action); + case 'double_click': + /** @var DoubleClickType $action */ + return DoubleClick::from($action); + case 'drag': + /** @var DragType $action */ + return Drag::from($action); + case 'keypress': + /** @var KeyPressType $action */ + return KeyPress::from($action); + case 'move': + /** @var MoveType $action */ + return Move::from($action); + case 'screenshot': + /** @var ScreenshotType $action */ + return Screenshot::from($action); + case 'scroll': + /** @var ScrollType $action */ + return Scroll::from($action); + case 'type': + /** @var TypeType $action */ + return Type::from($action); + case 'wait': + /** @var WaitType $action */ + return Wait::from($action); + default: + throw new \InvalidArgumentException('Invalid or missing action type in computer action payload.'); + } } }