codex-chatgpt-control 0.1.0a1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. codex_chatgpt_control-0.1.0a1/LICENSE +21 -0
  2. codex_chatgpt_control-0.1.0a1/PKG-INFO +151 -0
  3. codex_chatgpt_control-0.1.0a1/README.md +125 -0
  4. codex_chatgpt_control-0.1.0a1/pyproject.toml +47 -0
  5. codex_chatgpt_control-0.1.0a1/setup.cfg +4 -0
  6. codex_chatgpt_control-0.1.0a1/src/codex_chatgpt_control/__init__.py +101 -0
  7. codex_chatgpt_control-0.1.0a1/src/codex_chatgpt_control/agent.py +45 -0
  8. codex_chatgpt_control-0.1.0a1/src/codex_chatgpt_control/async_client.py +369 -0
  9. codex_chatgpt_control-0.1.0a1/src/codex_chatgpt_control/backend.py +338 -0
  10. codex_chatgpt_control-0.1.0a1/src/codex_chatgpt_control/client.py +152 -0
  11. codex_chatgpt_control-0.1.0a1/src/codex_chatgpt_control/commands.py +52 -0
  12. codex_chatgpt_control-0.1.0a1/src/codex_chatgpt_control/models.py +240 -0
  13. codex_chatgpt_control-0.1.0a1/src/codex_chatgpt_control/primitives.py +93 -0
  14. codex_chatgpt_control-0.1.0a1/src/codex_chatgpt_control/py.typed +1 -0
  15. codex_chatgpt_control-0.1.0a1/src/codex_chatgpt_control/reports.py +33 -0
  16. codex_chatgpt_control-0.1.0a1/src/codex_chatgpt_control/responses.py +235 -0
  17. codex_chatgpt_control-0.1.0a1/src/codex_chatgpt_control/runner.py +59 -0
  18. codex_chatgpt_control-0.1.0a1/src/codex_chatgpt_control/thread_entrypoint.py +433 -0
  19. codex_chatgpt_control-0.1.0a1/src/codex_chatgpt_control/transport.py +42 -0
  20. codex_chatgpt_control-0.1.0a1/src/codex_chatgpt_control/workflows.py +52 -0
  21. codex_chatgpt_control-0.1.0a1/src/codex_chatgpt_control.egg-info/PKG-INFO +151 -0
  22. codex_chatgpt_control-0.1.0a1/src/codex_chatgpt_control.egg-info/SOURCES.txt +47 -0
  23. codex_chatgpt_control-0.1.0a1/src/codex_chatgpt_control.egg-info/dependency_links.txt +1 -0
  24. codex_chatgpt_control-0.1.0a1/src/codex_chatgpt_control.egg-info/entry_points.txt +2 -0
  25. codex_chatgpt_control-0.1.0a1/src/codex_chatgpt_control.egg-info/requires.txt +4 -0
  26. codex_chatgpt_control-0.1.0a1/src/codex_chatgpt_control.egg-info/top_level.txt +1 -0
  27. codex_chatgpt_control-0.1.0a1/tests/test_agent.py +44 -0
  28. codex_chatgpt_control-0.1.0a1/tests/test_async_client.py +207 -0
  29. codex_chatgpt_control-0.1.0a1/tests/test_backend_client.py +71 -0
  30. codex_chatgpt_control-0.1.0a1/tests/test_backend_transport.py +188 -0
  31. codex_chatgpt_control-0.1.0a1/tests/test_commands.py +55 -0
  32. codex_chatgpt_control-0.1.0a1/tests/test_contract_fixtures.py +30 -0
  33. codex_chatgpt_control-0.1.0a1/tests/test_cross_language_conformance.py +288 -0
  34. codex_chatgpt_control-0.1.0a1/tests/test_http_stdio_relay.py +203 -0
  35. codex_chatgpt_control-0.1.0a1/tests/test_live_backend_smoke.py +71 -0
  36. codex_chatgpt_control-0.1.0a1/tests/test_models_complete.py +127 -0
  37. codex_chatgpt_control-0.1.0a1/tests/test_models_conformance.py +95 -0
  38. codex_chatgpt_control-0.1.0a1/tests/test_parity_snapshots.py +91 -0
  39. codex_chatgpt_control-0.1.0a1/tests/test_parity_suite.py +49 -0
  40. codex_chatgpt_control-0.1.0a1/tests/test_primitives.py +53 -0
  41. codex_chatgpt_control-0.1.0a1/tests/test_reports.py +42 -0
  42. codex_chatgpt_control-0.1.0a1/tests/test_responses.py +123 -0
  43. codex_chatgpt_control-0.1.0a1/tests/test_runner.py +88 -0
  44. codex_chatgpt_control-0.1.0a1/tests/test_runner_api.py +43 -0
  45. codex_chatgpt_control-0.1.0a1/tests/test_runner_streamed.py +44 -0
  46. codex_chatgpt_control-0.1.0a1/tests/test_streaming.py +47 -0
  47. codex_chatgpt_control-0.1.0a1/tests/test_thread_entrypoint.py +226 -0
  48. codex_chatgpt_control-0.1.0a1/tests/test_transport_sidecar.py +105 -0
  49. codex_chatgpt_control-0.1.0a1/tests/test_workflows.py +64 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Adam Allcock
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,151 @@
1
+ Metadata-Version: 2.4
2
+ Name: codex-chatgpt-control
3
+ Version: 0.1.0a1
4
+ Summary: Python SDK for Codex agents controlling visible ChatGPT web sessions through a local backend.
5
+ Author: Adam Allcock
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/adamallcock/codex-chatgpt-control
8
+ Project-URL: Issues, https://github.com/adamallcock/codex-chatgpt-control/issues
9
+ Project-URL: Source, https://github.com/adamallcock/codex-chatgpt-control
10
+ Keywords: codex,chatgpt,browser-automation,visible-ui,agents,sdk
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.10
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Programming Language :: Python :: 3.13
18
+ Classifier: Typing :: Typed
19
+ Requires-Python: >=3.10
20
+ Description-Content-Type: text/markdown
21
+ License-File: LICENSE
22
+ Requires-Dist: pydantic<3,>=2
23
+ Provides-Extra: dev
24
+ Requires-Dist: pyright>=1.1.390; extra == "dev"
25
+ Dynamic: license-file
26
+
27
+ # codex-chatgpt-control Python SDK
28
+
29
+ Python parity client for Codex agents controlling visible ChatGPT web sessions through the shared Node backend protocol.
30
+
31
+ Unofficial project: not affiliated with, endorsed by, or sponsored by OpenAI. This is not an OpenAI API wrapper and does not call hidden or private ChatGPT endpoints.
32
+
33
+ ```text
34
+ Python SDK -> backend protocol -> Node runtime -> browser bridge -> visible chatgpt.com session
35
+ ```
36
+
37
+ The current browser-control runtime is Node/TypeScript. Python talks to it through a long-lived local stdio backend service. This is intentionally not a pure-Python browser-control runtime yet.
38
+
39
+ ## Install
40
+
41
+ ```bash
42
+ python -m pip install codex-chatgpt-control
43
+ ```
44
+
45
+ The Python package needs a Node backend command for browser-control workflows. Install or build the Node package too:
46
+
47
+ ```bash
48
+ npm install codex-chatgpt-control
49
+ ```
50
+
51
+ ## Development Install
52
+
53
+ Build the backend bundle first:
54
+
55
+ ```bash
56
+ cd ../node
57
+ npm ci
58
+ npm run bundle:backend
59
+ ```
60
+
61
+ Install the Python package:
62
+
63
+ ```bash
64
+ cd ../python
65
+ python -m pip install -e .[dev]
66
+ ```
67
+
68
+ ## Sync Usage
69
+
70
+ ```python
71
+ from codex_chatgpt_control import Agent, BackendClient, Runner, StdioBackendTransport
72
+
73
+ backend = BackendClient(StdioBackendTransport(
74
+ command=["node", "../node/dist/codex-chatgpt-control-backend.mjs"]
75
+ ))
76
+ runner = Runner(backend)
77
+ agent = Agent(name="reviewer", instructions="Review carefully.")
78
+
79
+ try:
80
+ result = runner.run_sync(agent, {
81
+ "input": "Reply with hi.",
82
+ "thread": {"type": "new"},
83
+ "response": {"format": "markdown"},
84
+ })
85
+ finally:
86
+ backend.close()
87
+
88
+ print(result.status)
89
+ print(result.output_text)
90
+ ```
91
+
92
+ ## Agents-Style API
93
+
94
+ The Python SDK exposes OpenAI Agents SDK-inspired names where they fit the visible-session product:
95
+
96
+ - `Agent`
97
+ - `Runner.run`
98
+ - `Runner.run_sync`
99
+ - `Runner.run_streamed`
100
+ - `RunResult`
101
+ - `RunResultStreaming`
102
+
103
+ The semantics are browser-control semantics, not OpenAI API semantics. Instructions are visible by default and are submitted to ChatGPT web as prompt text unless `instructions_mode="metadata_only"` is used.
104
+
105
+ ## Product-Specific API
106
+
107
+ The `ChatGPT` facade exposes workflows and primitive command groups:
108
+
109
+ - `chatgpt.responses.create(...)`
110
+ - `chatgpt.ask(...)`, `ask_in_thread(...)`, `ask_with_files(...)`
111
+ - `chatgpt.run_plan({"name": "new-ask-read", ...})`
112
+ - `chatgpt.doctor(...)`
113
+ - `chatgpt.reports.create(...)`
114
+ - `chatgpt.session`, `threads`, `messages`, `files`, `modes`, `tools`, `response`
115
+ - `chatgpt.commands()`, `describe(...)`, `help(...)`
116
+
117
+ Unsupported OpenAI API-only Responses fields, such as `model`, `temperature`, and `previous_response_id`, return explicit unsupported responses instead of silently submitting misleading prompts.
118
+
119
+ ## Backend And Browser Bridge
120
+
121
+ Ordinary shells can launch the backend and validate the protocol. Browser-required calls need a compatible browser bridge.
122
+
123
+ Without a bridge, live browser operations should return:
124
+
125
+ ```json
126
+ {
127
+ "kind": "browser_bridge_unavailable"
128
+ }
129
+ ```
130
+
131
+ That blocker is expected in ordinary shells. A real live browser pass requires a backend command with bridge access. A plain Python-spawned Node subprocess does not automatically inherit a Codex browser bridge.
132
+
133
+ Override the backend command when needed:
134
+
135
+ ```bash
136
+ CHATGPT_BROWSER_BACKEND_COMMAND="node /absolute/path/to/bridge-enabled-backend.mjs" \
137
+ python scripts/live_smoke.py --mode browser-bridge
138
+ ```
139
+
140
+ ## Validation
141
+
142
+ Run from `packages/python`:
143
+
144
+ ```bash
145
+ python -m unittest discover -s tests
146
+ python -m compileall -q src examples
147
+ python -m pyright --pythonpath "$(which python)" src tests
148
+ python scripts/live_smoke.py --mode ordinary-shell
149
+ ```
150
+
151
+ The ordinary-shell smoke succeeds when the backend stays alive, `backend.health` succeeds, command descriptors load, and browser-required calls return `browser_bridge_unavailable`.
@@ -0,0 +1,125 @@
1
+ # codex-chatgpt-control Python SDK
2
+
3
+ Python parity client for Codex agents controlling visible ChatGPT web sessions through the shared Node backend protocol.
4
+
5
+ Unofficial project: not affiliated with, endorsed by, or sponsored by OpenAI. This is not an OpenAI API wrapper and does not call hidden or private ChatGPT endpoints.
6
+
7
+ ```text
8
+ Python SDK -> backend protocol -> Node runtime -> browser bridge -> visible chatgpt.com session
9
+ ```
10
+
11
+ The current browser-control runtime is Node/TypeScript. Python talks to it through a long-lived local stdio backend service. This is intentionally not a pure-Python browser-control runtime yet.
12
+
13
+ ## Install
14
+
15
+ ```bash
16
+ python -m pip install codex-chatgpt-control
17
+ ```
18
+
19
+ The Python package needs a Node backend command for browser-control workflows. Install or build the Node package too:
20
+
21
+ ```bash
22
+ npm install codex-chatgpt-control
23
+ ```
24
+
25
+ ## Development Install
26
+
27
+ Build the backend bundle first:
28
+
29
+ ```bash
30
+ cd ../node
31
+ npm ci
32
+ npm run bundle:backend
33
+ ```
34
+
35
+ Install the Python package:
36
+
37
+ ```bash
38
+ cd ../python
39
+ python -m pip install -e .[dev]
40
+ ```
41
+
42
+ ## Sync Usage
43
+
44
+ ```python
45
+ from codex_chatgpt_control import Agent, BackendClient, Runner, StdioBackendTransport
46
+
47
+ backend = BackendClient(StdioBackendTransport(
48
+ command=["node", "../node/dist/codex-chatgpt-control-backend.mjs"]
49
+ ))
50
+ runner = Runner(backend)
51
+ agent = Agent(name="reviewer", instructions="Review carefully.")
52
+
53
+ try:
54
+ result = runner.run_sync(agent, {
55
+ "input": "Reply with hi.",
56
+ "thread": {"type": "new"},
57
+ "response": {"format": "markdown"},
58
+ })
59
+ finally:
60
+ backend.close()
61
+
62
+ print(result.status)
63
+ print(result.output_text)
64
+ ```
65
+
66
+ ## Agents-Style API
67
+
68
+ The Python SDK exposes OpenAI Agents SDK-inspired names where they fit the visible-session product:
69
+
70
+ - `Agent`
71
+ - `Runner.run`
72
+ - `Runner.run_sync`
73
+ - `Runner.run_streamed`
74
+ - `RunResult`
75
+ - `RunResultStreaming`
76
+
77
+ The semantics are browser-control semantics, not OpenAI API semantics. Instructions are visible by default and are submitted to ChatGPT web as prompt text unless `instructions_mode="metadata_only"` is used.
78
+
79
+ ## Product-Specific API
80
+
81
+ The `ChatGPT` facade exposes workflows and primitive command groups:
82
+
83
+ - `chatgpt.responses.create(...)`
84
+ - `chatgpt.ask(...)`, `ask_in_thread(...)`, `ask_with_files(...)`
85
+ - `chatgpt.run_plan({"name": "new-ask-read", ...})`
86
+ - `chatgpt.doctor(...)`
87
+ - `chatgpt.reports.create(...)`
88
+ - `chatgpt.session`, `threads`, `messages`, `files`, `modes`, `tools`, `response`
89
+ - `chatgpt.commands()`, `describe(...)`, `help(...)`
90
+
91
+ Unsupported OpenAI API-only Responses fields, such as `model`, `temperature`, and `previous_response_id`, return explicit unsupported responses instead of silently submitting misleading prompts.
92
+
93
+ ## Backend And Browser Bridge
94
+
95
+ Ordinary shells can launch the backend and validate the protocol. Browser-required calls need a compatible browser bridge.
96
+
97
+ Without a bridge, live browser operations should return:
98
+
99
+ ```json
100
+ {
101
+ "kind": "browser_bridge_unavailable"
102
+ }
103
+ ```
104
+
105
+ That blocker is expected in ordinary shells. A real live browser pass requires a backend command with bridge access. A plain Python-spawned Node subprocess does not automatically inherit a Codex browser bridge.
106
+
107
+ Override the backend command when needed:
108
+
109
+ ```bash
110
+ CHATGPT_BROWSER_BACKEND_COMMAND="node /absolute/path/to/bridge-enabled-backend.mjs" \
111
+ python scripts/live_smoke.py --mode browser-bridge
112
+ ```
113
+
114
+ ## Validation
115
+
116
+ Run from `packages/python`:
117
+
118
+ ```bash
119
+ python -m unittest discover -s tests
120
+ python -m compileall -q src examples
121
+ python -m pyright --pythonpath "$(which python)" src tests
122
+ python scripts/live_smoke.py --mode ordinary-shell
123
+ ```
124
+
125
+ The ordinary-shell smoke succeeds when the backend stays alive, `backend.health` succeeds, command descriptors load, and browser-required calls return `browser_bridge_unavailable`.
@@ -0,0 +1,47 @@
1
+ [project]
2
+ name = "codex-chatgpt-control"
3
+ version = "0.1.0a1"
4
+ description = "Python SDK for Codex agents controlling visible ChatGPT web sessions through a local backend."
5
+ readme = "README.md"
6
+ requires-python = ">=3.10"
7
+ license = "MIT"
8
+ authors = [
9
+ { name = "Adam Allcock" }
10
+ ]
11
+ keywords = ["codex", "chatgpt", "browser-automation", "visible-ui", "agents", "sdk"]
12
+ classifiers = [
13
+ "Development Status :: 3 - Alpha",
14
+ "Intended Audience :: Developers",
15
+ "Programming Language :: Python :: 3",
16
+ "Programming Language :: Python :: 3.10",
17
+ "Programming Language :: Python :: 3.11",
18
+ "Programming Language :: Python :: 3.12",
19
+ "Programming Language :: Python :: 3.13",
20
+ "Typing :: Typed"
21
+ ]
22
+ dependencies = [
23
+ "pydantic>=2,<3"
24
+ ]
25
+
26
+ [project.scripts]
27
+ chatgpt-thread = "codex_chatgpt_control.thread_entrypoint:main"
28
+
29
+ [project.urls]
30
+ Homepage = "https://github.com/adamallcock/codex-chatgpt-control"
31
+ Issues = "https://github.com/adamallcock/codex-chatgpt-control/issues"
32
+ Source = "https://github.com/adamallcock/codex-chatgpt-control"
33
+
34
+ [project.optional-dependencies]
35
+ dev = [
36
+ "pyright>=1.1.390"
37
+ ]
38
+
39
+ [build-system]
40
+ requires = ["setuptools>=68"]
41
+ build-backend = "setuptools.build_meta"
42
+
43
+ [tool.setuptools.packages.find]
44
+ where = ["src"]
45
+
46
+ [tool.setuptools.package-data]
47
+ codex_chatgpt_control = ["py.typed"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,101 @@
1
+ from .async_client import (
2
+ AsyncChatGPT,
3
+ AsyncChatGPTRunner,
4
+ AsyncCommandClient,
5
+ AsyncPrimitiveGroup,
6
+ AsyncReportsClient,
7
+ AsyncResponsesClient,
8
+ AsyncRunResultStreaming,
9
+ AsyncWorkflowClient,
10
+ )
11
+ from .agent import Agent, AgentConfig
12
+ from .backend import (
13
+ BACKEND_EVENT_SCHEMA_VERSION,
14
+ BACKEND_REQUEST_SCHEMA_VERSION,
15
+ BACKEND_RESPONSE_SCHEMA_VERSION,
16
+ BackendClient,
17
+ BackendProtocolError,
18
+ BackendRequest,
19
+ BackendTransportError,
20
+ StdioBackendTransport,
21
+ )
22
+ from .client import ChatGPT, ChatGPTAgent, ChatGPTRunner
23
+ from .commands import CommandClient
24
+ from .models import (
25
+ BackendCapabilities,
26
+ BackendEvent,
27
+ BackendResponse,
28
+ ChatGPTAgentModel,
29
+ ChatGPTResponse,
30
+ ChatGPTRunInput,
31
+ ChatGPTRunResult,
32
+ ChatGPTRunState,
33
+ ChatGPTStreamEvent,
34
+ CommandDescriptor,
35
+ CommandResult,
36
+ DoctorReport,
37
+ RunReportData,
38
+ SequencePlan,
39
+ )
40
+ from .transport import NodeSidecarError, NodeSidecarTransport
41
+ from .runner import RunResult, RunResultStreaming, RunState, Runner
42
+ from .responses import ResponsesClient, ResponsesValidationResult
43
+ from .primitives import FilesClient, MessagesClient, ModesClient, ResponseClient, SessionClient, ThreadsClient, ToolsClient
44
+ from .reports import ReportsClient
45
+ from .workflows import WorkflowClient
46
+
47
+ __all__ = [
48
+ "Agent",
49
+ "AgentConfig",
50
+ "AsyncChatGPT",
51
+ "AsyncChatGPTRunner",
52
+ "AsyncCommandClient",
53
+ "AsyncPrimitiveGroup",
54
+ "AsyncReportsClient",
55
+ "AsyncResponsesClient",
56
+ "AsyncRunResultStreaming",
57
+ "AsyncWorkflowClient",
58
+ "BACKEND_EVENT_SCHEMA_VERSION",
59
+ "BACKEND_REQUEST_SCHEMA_VERSION",
60
+ "BACKEND_RESPONSE_SCHEMA_VERSION",
61
+ "BackendClient",
62
+ "BackendProtocolError",
63
+ "BackendRequest",
64
+ "BackendTransportError",
65
+ "BackendCapabilities",
66
+ "BackendEvent",
67
+ "BackendResponse",
68
+ "ChatGPT",
69
+ "ChatGPTAgentModel",
70
+ "ChatGPTAgent",
71
+ "ChatGPTRunner",
72
+ "ChatGPTRunInput",
73
+ "ChatGPTResponse",
74
+ "ChatGPTRunResult",
75
+ "ChatGPTRunState",
76
+ "ChatGPTStreamEvent",
77
+ "CommandClient",
78
+ "CommandDescriptor",
79
+ "CommandResult",
80
+ "DoctorReport",
81
+ "FilesClient",
82
+ "MessagesClient",
83
+ "ModesClient",
84
+ "NodeSidecarError",
85
+ "NodeSidecarTransport",
86
+ "ReportsClient",
87
+ "ResponseClient",
88
+ "RunReportData",
89
+ "RunResult",
90
+ "RunResultStreaming",
91
+ "RunState",
92
+ "Runner",
93
+ "ResponsesClient",
94
+ "ResponsesValidationResult",
95
+ "SessionClient",
96
+ "SequencePlan",
97
+ "StdioBackendTransport",
98
+ "ThreadsClient",
99
+ "ToolsClient",
100
+ "WorkflowClient",
101
+ ]
@@ -0,0 +1,45 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass, field
4
+ from typing import Any, Literal
5
+
6
+
7
+ InstructionsMode = Literal["visible_prefix", "visible_setup_message", "metadata_only"]
8
+
9
+
10
+ @dataclass(frozen=True)
11
+ class Agent:
12
+ name: str
13
+ instructions: str | None = None
14
+ instructions_mode: InstructionsMode = "visible_prefix"
15
+ defaults: dict[str, Any] = field(default_factory=dict)
16
+ tools: list[dict[str, Any]] = field(default_factory=list)
17
+ guardrails: list[dict[str, Any]] = field(default_factory=list)
18
+ output: dict[str, Any] | None = None
19
+ metadata: dict[str, Any] | None = None
20
+
21
+ def __post_init__(self) -> None:
22
+ normalized = self.name.strip()
23
+ if not normalized:
24
+ raise ValueError("Agent name must not be empty.")
25
+ object.__setattr__(self, "name", normalized)
26
+
27
+ def to_wire(self) -> dict[str, Any]:
28
+ payload: dict[str, Any] = {
29
+ "kind": "chatgpt_browser_agent",
30
+ "name": self.name,
31
+ "instructionsMode": self.instructions_mode,
32
+ "defaults": self.defaults,
33
+ "tools": self.tools,
34
+ "guardrails": self.guardrails,
35
+ }
36
+ if self.instructions is not None:
37
+ payload["instructions"] = self.instructions
38
+ if self.output is not None:
39
+ payload["output"] = self.output
40
+ if self.metadata is not None:
41
+ payload["metadata"] = self.metadata
42
+ return payload
43
+
44
+
45
+ AgentConfig = Agent