codexed 0.0.2__tar.gz → 0.0.4__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.
- {codexed-0.0.2 → codexed-0.0.4}/PKG-INFO +1 -1
- {codexed-0.0.2 → codexed-0.0.4}/pyproject.toml +1 -1
- {codexed-0.0.2 → codexed-0.0.4}/src/codexed/client.py +19 -11
- {codexed-0.0.2 → codexed-0.0.4}/src/codexed/models/request_params.py +73 -19
- {codexed-0.0.2 → codexed-0.0.4}/src/codexed/models/responses.py +10 -0
- {codexed-0.0.2 → codexed-0.0.4}/src/codexed/request_handlers.py +45 -5
- {codexed-0.0.2 → codexed-0.0.4}/LICENSE +0 -0
- {codexed-0.0.2 → codexed-0.0.4}/README.md +0 -0
- {codexed-0.0.2 → codexed-0.0.4}/src/codexed/README.md +0 -0
- {codexed-0.0.2 → codexed-0.0.4}/src/codexed/__init__.py +0 -0
- {codexed-0.0.2 → codexed-0.0.4}/src/codexed/example.py +0 -0
- {codexed-0.0.2 → codexed-0.0.4}/src/codexed/exceptions.py +0 -0
- {codexed-0.0.2 → codexed-0.0.4}/src/codexed/helpers.py +0 -0
- {codexed-0.0.2 → codexed-0.0.4}/src/codexed/log.py +0 -0
- {codexed-0.0.2 → codexed-0.0.4}/src/codexed/models/__init__.py +0 -0
- {codexed-0.0.2 → codexed-0.0.4}/src/codexed/models/base.py +0 -0
- {codexed-0.0.2 → codexed-0.0.4}/src/codexed/models/codex_types.py +0 -0
- {codexed-0.0.2 → codexed-0.0.4}/src/codexed/models/command_action.py +0 -0
- {codexed-0.0.2 → codexed-0.0.4}/src/codexed/models/event_data.py +0 -0
- {codexed-0.0.2 → codexed-0.0.4}/src/codexed/models/events.py +0 -0
- {codexed-0.0.2 → codexed-0.0.4}/src/codexed/models/mcp_server.py +0 -0
- {codexed-0.0.2 → codexed-0.0.4}/src/codexed/models/misc.py +0 -0
- {codexed-0.0.2 → codexed-0.0.4}/src/codexed/models/response_item.py +0 -0
- {codexed-0.0.2 → codexed-0.0.4}/src/codexed/models/thread_item.py +0 -0
- {codexed-0.0.2 → codexed-0.0.4}/src/codexed/models/thread_status.py +0 -0
- {codexed-0.0.2 → codexed-0.0.4}/src/codexed/models/token_usage.py +0 -0
- {codexed-0.0.2 → codexed-0.0.4}/src/codexed/models/tool_config.py +0 -0
- {codexed-0.0.2 → codexed-0.0.4}/src/codexed/models/user_input.py +0 -0
- {codexed-0.0.2 → codexed-0.0.4}/src/codexed/models/web_search.py +0 -0
- {codexed-0.0.2 → codexed-0.0.4}/src/codexed/py.typed +0 -0
|
@@ -104,6 +104,8 @@ from codexed.request_handlers import (
|
|
|
104
104
|
SERVER_REQUEST_COMMAND_APPROVAL,
|
|
105
105
|
SERVER_REQUEST_DYNAMIC_TOOL_CALL,
|
|
106
106
|
SERVER_REQUEST_FILE_CHANGE_APPROVAL,
|
|
107
|
+
SERVER_REQUEST_MCP_ELICITATION,
|
|
108
|
+
SERVER_REQUEST_SKILL_APPROVAL,
|
|
107
109
|
SERVER_REQUEST_TYPES,
|
|
108
110
|
SERVER_REQUEST_USER_INPUT,
|
|
109
111
|
create_auto_approve_dict,
|
|
@@ -141,10 +143,10 @@ if TYPE_CHECKING:
|
|
|
141
143
|
)
|
|
142
144
|
from codexed.models.request_params import HazelnutScope, LoginType, ProductSurface
|
|
143
145
|
from codexed.request_handlers import (
|
|
144
|
-
|
|
146
|
+
ApprovalHandler,
|
|
145
147
|
DynamicToolCallHandler,
|
|
146
|
-
FileChangeApprovalHandler,
|
|
147
148
|
HandlerMethod,
|
|
149
|
+
McpElicitationHandler,
|
|
148
150
|
ServerRequestHandler,
|
|
149
151
|
UserInputHandler,
|
|
150
152
|
)
|
|
@@ -354,10 +356,10 @@ class CodexClient:
|
|
|
354
356
|
profile: str | None = None,
|
|
355
357
|
env_vars: dict[str, str] | None = None,
|
|
356
358
|
mcp_servers: Mapping[str, McpServerConfig] | None = None,
|
|
357
|
-
|
|
358
|
-
on_file_change_approval: FileChangeApprovalHandler | None = None,
|
|
359
|
+
on_approval: ApprovalHandler | None = None,
|
|
359
360
|
on_user_input: UserInputHandler | None = None,
|
|
360
361
|
on_dynamic_tool_call: DynamicToolCallHandler | None = None,
|
|
362
|
+
on_mcp_elicitation: McpElicitationHandler | None = None,
|
|
361
363
|
) -> None:
|
|
362
364
|
"""Initialize the Codex app-server client.
|
|
363
365
|
|
|
@@ -367,10 +369,11 @@ class CodexClient:
|
|
|
367
369
|
env_vars: Optional environment variables to set for the Codex process.
|
|
368
370
|
mcp_servers: Optional MCP servers to inject programmatically.
|
|
369
371
|
Keys are server names, values are server configurations.
|
|
370
|
-
|
|
371
|
-
|
|
372
|
+
on_approval: Handler for all approval requests
|
|
373
|
+
(command execution, file change, and skill approval).
|
|
372
374
|
on_user_input: Handler for tool user input requests.
|
|
373
375
|
on_dynamic_tool_call: Handler for dynamic tool call requests.
|
|
376
|
+
on_mcp_elicitation: Handler for MCP elicitation requests.
|
|
374
377
|
"""
|
|
375
378
|
self._codex_command = codex_command
|
|
376
379
|
self._profile = profile
|
|
@@ -385,14 +388,16 @@ class CodexClient:
|
|
|
385
388
|
self._writer_lock = asyncio.Lock()
|
|
386
389
|
self._active_threads: set[str] = set()
|
|
387
390
|
self._server_request_handlers: dict[str, ServerRequestHandler] = {}
|
|
388
|
-
if
|
|
389
|
-
self.register_handler(SERVER_REQUEST_COMMAND_APPROVAL,
|
|
390
|
-
|
|
391
|
-
self.register_handler(
|
|
391
|
+
if on_approval:
|
|
392
|
+
self.register_handler(SERVER_REQUEST_COMMAND_APPROVAL, on_approval) # type: ignore[arg-type]
|
|
393
|
+
self.register_handler(SERVER_REQUEST_FILE_CHANGE_APPROVAL, on_approval) # type: ignore[arg-type]
|
|
394
|
+
self.register_handler(SERVER_REQUEST_SKILL_APPROVAL, on_approval) # type: ignore[arg-type]
|
|
392
395
|
if on_user_input:
|
|
393
396
|
self.register_handler(SERVER_REQUEST_USER_INPUT, on_user_input) # type: ignore[arg-type]
|
|
394
397
|
if on_dynamic_tool_call:
|
|
395
398
|
self.register_handler(SERVER_REQUEST_DYNAMIC_TOOL_CALL, on_dynamic_tool_call) # type: ignore[arg-type]
|
|
399
|
+
if on_mcp_elicitation:
|
|
400
|
+
self.register_handler(SERVER_REQUEST_MCP_ELICITATION, on_mcp_elicitation) # type: ignore[arg-type]
|
|
396
401
|
|
|
397
402
|
async def __aenter__(self) -> Self:
|
|
398
403
|
"""Async context manager entry - starts the app-server."""
|
|
@@ -1768,7 +1773,10 @@ class CodexClient:
|
|
|
1768
1773
|
return
|
|
1769
1774
|
|
|
1770
1775
|
try:
|
|
1771
|
-
|
|
1776
|
+
if isinstance(params_type, TypeAdapter):
|
|
1777
|
+
parsed_params = params_type.validate_python(params)
|
|
1778
|
+
else:
|
|
1779
|
+
parsed_params = params_type.model_validate(params)
|
|
1772
1780
|
response_model = await handler(parsed_params)
|
|
1773
1781
|
await self._send_server_request_response(request_id, response_model)
|
|
1774
1782
|
except Exception:
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
from collections.abc import Sequence # noqa: TC003
|
|
4
|
-
from typing import Any, Literal, Self
|
|
4
|
+
from typing import Annotated, Any, Literal, Self
|
|
5
|
+
|
|
6
|
+
import mcp.types
|
|
7
|
+
from pydantic import Discriminator, TypeAdapter
|
|
5
8
|
|
|
6
9
|
from codexed.models.base import CodexBaseModel
|
|
7
10
|
from codexed.models.codex_types import ( # noqa: TC001
|
|
@@ -507,34 +510,85 @@ McpElicitationAction = Literal["accept", "decline", "cancel"]
|
|
|
507
510
|
"""Action taken by the user on an elicitation request."""
|
|
508
511
|
|
|
509
512
|
|
|
510
|
-
class
|
|
511
|
-
"""
|
|
513
|
+
class _McpElicitationBase(CodexBaseModel):
|
|
514
|
+
"""Common fields for MCP elicitation requests."""
|
|
512
515
|
|
|
513
|
-
|
|
516
|
+
thread_id: str
|
|
517
|
+
turn_id: str | None = None
|
|
518
|
+
server_name: str
|
|
514
519
|
meta: Any | None = None
|
|
515
520
|
message: str
|
|
521
|
+
|
|
522
|
+
|
|
523
|
+
class McpElicitationFormParams(_McpElicitationBase):
|
|
524
|
+
"""Form-mode MCP elicitation request."""
|
|
525
|
+
|
|
526
|
+
mode: Literal["form"] = "form"
|
|
516
527
|
requested_schema: dict[str, Any]
|
|
517
528
|
|
|
529
|
+
def to_mcp(self) -> mcp.types.ElicitRequestFormParams:
|
|
530
|
+
return mcp.types.ElicitRequestFormParams(
|
|
531
|
+
message=self.message,
|
|
532
|
+
requestedSchema=self.requested_schema,
|
|
533
|
+
)
|
|
518
534
|
|
|
519
|
-
|
|
535
|
+
@classmethod
|
|
536
|
+
def from_mcp(
|
|
537
|
+
cls,
|
|
538
|
+
params: mcp.types.ElicitRequestFormParams,
|
|
539
|
+
thread_id: str,
|
|
540
|
+
server_name: str,
|
|
541
|
+
turn_id: str | None = None,
|
|
542
|
+
) -> McpElicitationFormParams:
|
|
543
|
+
"""Create from MCP ElicitRequestFormParams."""
|
|
544
|
+
return cls(
|
|
545
|
+
message=params.message,
|
|
546
|
+
requested_schema=dict(params.requestedSchema),
|
|
547
|
+
thread_id=thread_id,
|
|
548
|
+
server_name=server_name,
|
|
549
|
+
turn_id=turn_id,
|
|
550
|
+
)
|
|
551
|
+
|
|
552
|
+
|
|
553
|
+
class McpElicitationUrlParams(_McpElicitationBase):
|
|
520
554
|
"""URL-mode MCP elicitation request (browser-based auth)."""
|
|
521
555
|
|
|
522
556
|
mode: Literal["url"] = "url"
|
|
523
|
-
meta: Any | None = None
|
|
524
|
-
message: str
|
|
525
557
|
url: str
|
|
526
558
|
elicitation_id: str
|
|
527
559
|
|
|
560
|
+
def to_mcp(self) -> mcp.types.ElicitRequestURLParams:
|
|
561
|
+
return mcp.types.ElicitRequestURLParams(
|
|
562
|
+
message=self.message,
|
|
563
|
+
url=self.url,
|
|
564
|
+
elicitationId=self.elicitation_id,
|
|
565
|
+
)
|
|
528
566
|
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
567
|
+
@classmethod
|
|
568
|
+
def from_mcp(
|
|
569
|
+
cls,
|
|
570
|
+
params: mcp.types.ElicitRequestURLParams,
|
|
571
|
+
thread_id: str,
|
|
572
|
+
server_name: str,
|
|
573
|
+
turn_id: str | None = None,
|
|
574
|
+
) -> McpElicitationUrlParams:
|
|
575
|
+
"""Create from MCP ElicitRequestURLParams."""
|
|
576
|
+
return cls(
|
|
577
|
+
message=params.message,
|
|
578
|
+
url=params.url,
|
|
579
|
+
elicitation_id=params.elicitationId,
|
|
580
|
+
thread_id=thread_id,
|
|
581
|
+
server_name=server_name,
|
|
582
|
+
turn_id=turn_id,
|
|
583
|
+
)
|
|
584
|
+
|
|
585
|
+
|
|
586
|
+
McpServerElicitationRequestParams = Annotated[
|
|
587
|
+
McpElicitationFormParams | McpElicitationUrlParams,
|
|
588
|
+
Discriminator("mode"),
|
|
589
|
+
]
|
|
590
|
+
"""Parameters for mcpServer/elicitation/request, discriminated by mode."""
|
|
591
|
+
|
|
592
|
+
mcp_elicitation_adapter: TypeAdapter[McpServerElicitationRequestParams] = TypeAdapter(
|
|
593
|
+
McpServerElicitationRequestParams
|
|
594
|
+
)
|
|
@@ -2,6 +2,8 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
from typing import Any, Literal
|
|
4
4
|
|
|
5
|
+
import mcp.types
|
|
6
|
+
|
|
5
7
|
from codexed.models.base import CodexBaseModel
|
|
6
8
|
from codexed.models.codex_types import ( # noqa: TC001
|
|
7
9
|
AskForApproval,
|
|
@@ -62,6 +64,14 @@ class McpServerElicitationResponse(CodexBaseModel):
|
|
|
62
64
|
content: Any | None = None
|
|
63
65
|
meta: Any | None = None
|
|
64
66
|
|
|
67
|
+
def to_mcp(self) -> mcp.types.ElicitResult:
|
|
68
|
+
return mcp.types.ElicitResult(action=self.action, content=self.content)
|
|
69
|
+
|
|
70
|
+
@classmethod
|
|
71
|
+
def from_mcp(cls, result: mcp.types.ElicitResult) -> McpServerElicitationResponse:
|
|
72
|
+
"""Create from MCP ElicitResult."""
|
|
73
|
+
return cls(action=result.action, content=result.content)
|
|
74
|
+
|
|
65
75
|
|
|
66
76
|
class ThreadReadResponse(CodexBaseModel):
|
|
67
77
|
"""Response for thread/read request."""
|
|
@@ -5,6 +5,8 @@ from __future__ import annotations
|
|
|
5
5
|
from collections.abc import Awaitable, Callable
|
|
6
6
|
from typing import Literal
|
|
7
7
|
|
|
8
|
+
from pydantic import TypeAdapter
|
|
9
|
+
|
|
8
10
|
from codexed.models import (
|
|
9
11
|
CommandExecutionRequestApprovalParams,
|
|
10
12
|
CommandExecutionRequestApprovalResponse,
|
|
@@ -12,11 +14,18 @@ from codexed.models import (
|
|
|
12
14
|
DynamicToolCallResponse,
|
|
13
15
|
FileChangeRequestApprovalParams,
|
|
14
16
|
FileChangeRequestApprovalResponse,
|
|
15
|
-
McpServerElicitationRequestParams,
|
|
16
17
|
McpServerElicitationResponse,
|
|
18
|
+
SkillRequestApprovalParams,
|
|
19
|
+
SkillRequestApprovalResponse,
|
|
17
20
|
ToolRequestUserInputParams,
|
|
18
21
|
ToolRequestUserInputResponse,
|
|
19
22
|
)
|
|
23
|
+
from codexed.models.request_params import (
|
|
24
|
+
McpElicitationFormParams,
|
|
25
|
+
McpElicitationUrlParams,
|
|
26
|
+
McpServerElicitationRequestParams,
|
|
27
|
+
mcp_elicitation_adapter,
|
|
28
|
+
)
|
|
20
29
|
|
|
21
30
|
|
|
22
31
|
# Server request method constants
|
|
@@ -24,6 +33,7 @@ SERVER_REQUEST_COMMAND_APPROVAL = "item/commandExecution/requestApproval"
|
|
|
24
33
|
SERVER_REQUEST_FILE_CHANGE_APPROVAL = "item/fileChange/requestApproval"
|
|
25
34
|
SERVER_REQUEST_USER_INPUT = "item/tool/requestUserInput"
|
|
26
35
|
SERVER_REQUEST_DYNAMIC_TOOL_CALL = "item/tool/call"
|
|
36
|
+
SERVER_REQUEST_SKILL_APPROVAL = "skill/requestApproval"
|
|
27
37
|
SERVER_REQUEST_MCP_ELICITATION = "mcpServer/elicitation/request"
|
|
28
38
|
|
|
29
39
|
HandlerMethod = Literal[
|
|
@@ -31,21 +41,25 @@ HandlerMethod = Literal[
|
|
|
31
41
|
"item/fileChange/requestApproval",
|
|
32
42
|
"item/tool/requestUserInput",
|
|
33
43
|
"item/tool/call",
|
|
44
|
+
"skill/requestApproval",
|
|
34
45
|
"mcpServer/elicitation/request",
|
|
35
46
|
]
|
|
36
47
|
# Type for server request parameter models
|
|
37
48
|
ServerRequestParams = (
|
|
38
49
|
CommandExecutionRequestApprovalParams
|
|
39
50
|
| FileChangeRequestApprovalParams
|
|
51
|
+
| SkillRequestApprovalParams
|
|
40
52
|
| ToolRequestUserInputParams
|
|
41
53
|
| DynamicToolCallParams
|
|
42
|
-
|
|
|
54
|
+
| McpElicitationFormParams
|
|
55
|
+
| McpElicitationUrlParams
|
|
43
56
|
)
|
|
44
57
|
|
|
45
58
|
# Type for server request response models
|
|
46
59
|
ServerRequestResponse = (
|
|
47
60
|
CommandExecutionRequestApprovalResponse
|
|
48
61
|
| FileChangeRequestApprovalResponse
|
|
62
|
+
| SkillRequestApprovalResponse
|
|
49
63
|
| ToolRequestUserInputResponse
|
|
50
64
|
| DynamicToolCallResponse
|
|
51
65
|
| McpServerElicitationResponse
|
|
@@ -69,8 +83,24 @@ McpElicitationHandler = Callable[
|
|
|
69
83
|
[McpServerElicitationRequestParams], Awaitable[McpServerElicitationResponse]
|
|
70
84
|
]
|
|
71
85
|
|
|
72
|
-
#
|
|
73
|
-
|
|
86
|
+
# Unified approval handler: one callback for all approval types
|
|
87
|
+
ApprovalParams = (
|
|
88
|
+
CommandExecutionRequestApprovalParams
|
|
89
|
+
| FileChangeRequestApprovalParams
|
|
90
|
+
| SkillRequestApprovalParams
|
|
91
|
+
)
|
|
92
|
+
ApprovalResponse = (
|
|
93
|
+
CommandExecutionRequestApprovalResponse
|
|
94
|
+
| FileChangeRequestApprovalResponse
|
|
95
|
+
| SkillRequestApprovalResponse
|
|
96
|
+
)
|
|
97
|
+
ApprovalHandler = Callable[[ApprovalParams], Awaitable[ApprovalResponse]]
|
|
98
|
+
|
|
99
|
+
# Validator: either a model class or a TypeAdapter for discriminated unions
|
|
100
|
+
ParamsValidator = type[ServerRequestParams] | TypeAdapter[ServerRequestParams]
|
|
101
|
+
|
|
102
|
+
# Map from wire method names to param validator + response model type
|
|
103
|
+
SERVER_REQUEST_TYPES: dict[str, tuple[ParamsValidator, type[ServerRequestResponse]]] = {
|
|
74
104
|
SERVER_REQUEST_COMMAND_APPROVAL: (
|
|
75
105
|
CommandExecutionRequestApprovalParams,
|
|
76
106
|
CommandExecutionRequestApprovalResponse,
|
|
@@ -84,8 +114,12 @@ SERVER_REQUEST_TYPES: dict[str, tuple[type[ServerRequestParams], type[ServerRequ
|
|
|
84
114
|
ToolRequestUserInputResponse,
|
|
85
115
|
),
|
|
86
116
|
SERVER_REQUEST_DYNAMIC_TOOL_CALL: (DynamicToolCallParams, DynamicToolCallResponse),
|
|
117
|
+
SERVER_REQUEST_SKILL_APPROVAL: (
|
|
118
|
+
SkillRequestApprovalParams,
|
|
119
|
+
SkillRequestApprovalResponse,
|
|
120
|
+
),
|
|
87
121
|
SERVER_REQUEST_MCP_ELICITATION: (
|
|
88
|
-
|
|
122
|
+
mcp_elicitation_adapter, # type: ignore[dict-item]
|
|
89
123
|
McpServerElicitationResponse,
|
|
90
124
|
),
|
|
91
125
|
}
|
|
@@ -118,6 +152,11 @@ def create_auto_approve_dict() -> dict[str, ServerRequestHandler]:
|
|
|
118
152
|
) -> ServerRequestResponse:
|
|
119
153
|
return DynamicToolCallResponse(content_items=[], success=False)
|
|
120
154
|
|
|
155
|
+
async def auto_approve_skill(
|
|
156
|
+
_params: ServerRequestParams,
|
|
157
|
+
) -> ServerRequestResponse:
|
|
158
|
+
return SkillRequestApprovalResponse(decision="allow")
|
|
159
|
+
|
|
121
160
|
async def auto_decline_elicitation(
|
|
122
161
|
_params: ServerRequestParams,
|
|
123
162
|
) -> ServerRequestResponse:
|
|
@@ -128,5 +167,6 @@ def create_auto_approve_dict() -> dict[str, ServerRequestHandler]:
|
|
|
128
167
|
dct[SERVER_REQUEST_FILE_CHANGE_APPROVAL] = auto_approve_file_change
|
|
129
168
|
dct[SERVER_REQUEST_USER_INPUT] = auto_approve_user_input
|
|
130
169
|
dct[SERVER_REQUEST_DYNAMIC_TOOL_CALL] = auto_approve_dynamic_tool
|
|
170
|
+
dct[SERVER_REQUEST_SKILL_APPROVAL] = auto_approve_skill
|
|
131
171
|
dct[SERVER_REQUEST_MCP_ELICITATION] = auto_decline_elicitation
|
|
132
172
|
return dct
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|