python-codex 0.1.1__py3-none-any.whl → 0.1.3__py3-none-any.whl

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 (59) hide show
  1. pycodex/__init__.py +5 -1
  2. pycodex/agent.py +39 -41
  3. pycodex/cli.py +51 -43
  4. pycodex/collaboration.py +6 -7
  5. pycodex/compat.py +99 -0
  6. pycodex/context.py +87 -87
  7. pycodex/doctor.py +40 -40
  8. pycodex/model.py +69 -69
  9. pycodex/portable.py +33 -33
  10. pycodex/portable_server.py +22 -21
  11. pycodex/protocol.py +84 -86
  12. pycodex/runtime.py +36 -35
  13. pycodex/runtime_services.py +72 -69
  14. pycodex/tools/agent_tool_schemas.py +0 -2
  15. pycodex/tools/apply_patch_tool.py +43 -44
  16. pycodex/tools/base_tool.py +35 -36
  17. pycodex/tools/close_agent_tool.py +2 -4
  18. pycodex/tools/code_mode_manager.py +61 -61
  19. pycodex/tools/exec_command_tool.py +5 -6
  20. pycodex/tools/exec_runtime.js +3 -3
  21. pycodex/tools/exec_tool.py +3 -5
  22. pycodex/tools/grep_files_tool.py +10 -11
  23. pycodex/tools/list_dir_tool.py +8 -9
  24. pycodex/tools/read_file_tool.py +13 -14
  25. pycodex/tools/request_permissions_tool.py +2 -4
  26. pycodex/tools/request_user_input_tool.py +13 -14
  27. pycodex/tools/resume_agent_tool.py +2 -4
  28. pycodex/tools/send_input_tool.py +8 -9
  29. pycodex/tools/shell_command_tool.py +5 -6
  30. pycodex/tools/shell_tool.py +5 -6
  31. pycodex/tools/spawn_agent_tool.py +4 -5
  32. pycodex/tools/unified_exec_manager.py +79 -61
  33. pycodex/tools/update_plan_tool.py +4 -5
  34. pycodex/tools/view_image_tool.py +4 -5
  35. pycodex/tools/wait_agent_tool.py +2 -4
  36. pycodex/tools/wait_tool.py +4 -5
  37. pycodex/tools/web_search_tool.py +1 -3
  38. pycodex/tools/write_stdin_tool.py +4 -5
  39. pycodex/utils/dotenv.py +6 -6
  40. pycodex/utils/get_env.py +57 -34
  41. pycodex/utils/random_ids.py +1 -2
  42. pycodex/utils/visualize.py +79 -79
  43. {python_codex-0.1.1.dist-info → python_codex-0.1.3.dist-info}/METADATA +15 -9
  44. python_codex-0.1.3.dist-info/RECORD +74 -0
  45. {python_codex-0.1.1.dist-info → python_codex-0.1.3.dist-info}/WHEEL +1 -1
  46. responses_server/__init__.py +17 -0
  47. responses_server/__main__.py +5 -0
  48. responses_server/app.py +227 -0
  49. responses_server/config.py +63 -0
  50. responses_server/payload_processors.py +86 -0
  51. responses_server/server.py +63 -0
  52. responses_server/session_store.py +37 -0
  53. responses_server/stream_router.py +784 -0
  54. responses_server/tools/__init__.py +4 -0
  55. responses_server/tools/custom_adapter.py +235 -0
  56. responses_server/tools/web_search.py +263 -0
  57. python_codex-0.1.1.dist-info/RECORD +0 -62
  58. {python_codex-0.1.1.dist-info → python_codex-0.1.3.dist-info}/entry_points.txt +0 -0
  59. {python_codex-0.1.1.dist-info → python_codex-0.1.3.dist-info}/licenses/LICENSE +0 -0
@@ -1,4 +1,3 @@
1
- from __future__ import annotations
2
1
 
3
2
  import asyncio
4
3
  import json
@@ -12,6 +11,7 @@ from prompt_toolkit.patch_stdout import StdoutProxy
12
11
  from prompt_toolkit.patch_stdout import patch_stdout
13
12
 
14
13
  from ..protocol import AgentEvent, JSONDict, ToolCall, ToolResult
14
+ import typing
15
15
 
16
16
  ANSI_RESET = "\x1b[0m"
17
17
  ANSI_BOLD = "\x1b[1m"
@@ -25,14 +25,14 @@ ANSI_RED = "\x1b[31m"
25
25
  SPINNER_FRAMES = ("⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏")
26
26
 
27
27
 
28
- def shorten_title(text: str, limit: int = 48) -> str:
28
+ def shorten_title(text: 'str', limit: 'int' = 48) -> 'str':
29
29
  compact = " ".join(text.split())
30
30
  if len(compact) <= limit:
31
31
  return compact
32
32
  return compact[: limit - 3].rstrip() + "..."
33
33
 
34
34
 
35
- def cli_color_enabled() -> bool:
35
+ def cli_color_enabled() -> 'bool':
36
36
  return os.environ.get("PYCODEX_NO_COLOR", "").strip().lower() not in {
37
37
  "1",
38
38
  "true",
@@ -41,7 +41,7 @@ def cli_color_enabled() -> bool:
41
41
  }
42
42
 
43
43
 
44
- def colorize_cli_message(text: str, kind: str, enabled: bool) -> str:
44
+ def colorize_cli_message(text: 'str', kind: 'str', enabled: 'bool') -> 'str':
45
45
  if not enabled:
46
46
  return text
47
47
  palette = {
@@ -61,9 +61,9 @@ def colorize_cli_message(text: str, kind: str, enabled: bool) -> str:
61
61
 
62
62
 
63
63
  def format_cli_plan_messages(
64
- summary: str,
65
- plan_items: list[JSONDict],
66
- ) -> list[str]:
64
+ summary: 'str',
65
+ plan_items: 'typing.List[JSONDict]',
66
+ ) -> 'typing.List[str]':
67
67
  lines = [f"[plan] {summary}" if summary else "[plan] Plan updated"]
68
68
  for item in plan_items:
69
69
  step = str(item.get("step", "")).strip()
@@ -79,7 +79,7 @@ def format_cli_plan_messages(
79
79
  return lines
80
80
 
81
81
 
82
- def build_cli_spinner_frame(index: int, label: str) -> str:
82
+ def build_cli_spinner_frame(index: 'int', label: 'str') -> 'str':
83
83
  suffix = f" {label}" if label else ""
84
84
  return f"⏳{suffix} {SPINNER_FRAMES[index % len(SPINNER_FRAMES)]}"
85
85
 
@@ -89,10 +89,10 @@ class Spinner:
89
89
  self,
90
90
  raw_write,
91
91
  raw_flush,
92
- terminal_lock: threading.RLock,
93
- color_enabled: bool,
94
- enabled: bool,
95
- ) -> None:
92
+ terminal_lock: 'threading.RLock',
93
+ color_enabled: 'bool',
94
+ enabled: 'bool',
95
+ ) -> 'None':
96
96
  self._raw_write = raw_write
97
97
  self._raw_flush = raw_flush
98
98
  self._terminal_lock = terminal_lock
@@ -104,7 +104,7 @@ class Spinner:
104
104
  self._index = 0
105
105
  self._label = "thinking"
106
106
  self._stop = threading.Event()
107
- self._thread: threading.Thread | None = None
107
+ self._thread: 'typing.Union[threading.Thread, None]' = None
108
108
  if self._enabled:
109
109
  self._thread = threading.Thread(
110
110
  target=self._run,
@@ -113,32 +113,32 @@ class Spinner:
113
113
  )
114
114
  self._thread.start()
115
115
 
116
- def start_turn(self, label: str = "thinking") -> None:
116
+ def start_turn(self, label: 'str' = "thinking") -> 'None':
117
117
  with self._terminal_lock:
118
118
  self._turn_active = True
119
119
  self._paused = False
120
120
  self._label = label
121
121
 
122
- def set_label(self, label: str) -> None:
122
+ def set_label(self, label: 'str') -> 'None':
123
123
  with self._terminal_lock:
124
124
  self._label = label
125
125
 
126
- def finish_turn(self) -> None:
126
+ def finish_turn(self) -> 'None':
127
127
  with self._terminal_lock:
128
128
  self._turn_active = False
129
129
  self._paused = False
130
130
  self.clear()
131
131
 
132
- def pause(self) -> None:
132
+ def pause(self) -> 'None':
133
133
  with self._terminal_lock:
134
134
  self._paused = True
135
135
  self.clear()
136
136
 
137
- def resume(self) -> None:
137
+ def resume(self) -> 'None':
138
138
  with self._terminal_lock:
139
139
  self._paused = False
140
140
 
141
- def clear(self) -> None:
141
+ def clear(self) -> 'None':
142
142
  if not self._enabled or not self._visible:
143
143
  return
144
144
  with self._terminal_lock:
@@ -146,13 +146,13 @@ class Spinner:
146
146
  self._raw_flush()
147
147
  self._visible = False
148
148
 
149
- def close(self) -> None:
149
+ def close(self) -> 'None':
150
150
  self.finish_turn()
151
151
  if self._thread is not None:
152
152
  self._stop.set()
153
153
  self._thread.join(timeout=0.5)
154
154
 
155
- def prompt_line(self) -> str | None:
155
+ def prompt_line(self) -> 'typing.Union[str, None]':
156
156
  if not self._turn_active:
157
157
  return None
158
158
  with self._terminal_lock:
@@ -160,7 +160,7 @@ class Spinner:
160
160
  frame_index = int(time.monotonic() / 0.12)
161
161
  return build_cli_spinner_frame(frame_index, label)
162
162
 
163
- def _run(self) -> None:
163
+ def _run(self) -> 'None':
164
164
  while not self._stop.wait(0.12):
165
165
  if not self._turn_active or self._paused:
166
166
  continue
@@ -178,7 +178,7 @@ class Spinner:
178
178
  self._visible = True
179
179
 
180
180
 
181
- def format_cli_tool_call_message(tool_name: str, payload: JSONDict) -> str | None:
181
+ def format_cli_tool_call_message(tool_name: 'str', payload: 'JSONDict') -> 'typing.Union[str, None]':
182
182
  if tool_name != "web_search":
183
183
  return None
184
184
 
@@ -207,7 +207,7 @@ def format_cli_tool_call_message(tool_name: str, payload: JSONDict) -> str | Non
207
207
  return "[web] browsing"
208
208
 
209
209
 
210
- def short_id(value: str, limit: int = 8) -> str:
210
+ def short_id(value: 'str', limit: 'int' = 8) -> 'str':
211
211
  compact = value.strip()
212
212
  if len(compact) <= limit + 4:
213
213
  return compact
@@ -215,10 +215,10 @@ def short_id(value: str, limit: int = 8) -> str:
215
215
 
216
216
 
217
217
  def format_cli_tool_message(
218
- tool_name: str,
219
- summary: str,
220
- is_error: bool,
221
- ) -> str:
218
+ tool_name: 'str',
219
+ summary: 'str',
220
+ is_error: 'bool',
221
+ ) -> 'str':
222
222
  if tool_name == "update_plan":
223
223
  if is_error:
224
224
  return f"[error] plan failed: {summary}" if summary else "[error] plan failed"
@@ -290,13 +290,13 @@ def format_cli_tool_message(
290
290
  return f"[tool] {tool_name}: {summary}" if summary else f"[tool] {tool_name}"
291
291
 
292
292
 
293
- def extract_plan_items(arguments: object) -> list[JSONDict]:
293
+ def extract_plan_items(arguments: 'object') -> 'typing.List[JSONDict]':
294
294
  if not isinstance(arguments, dict):
295
295
  return []
296
296
  raw_plan = arguments.get("plan")
297
297
  if not isinstance(raw_plan, list):
298
298
  return []
299
- plan_items: list[JSONDict] = []
299
+ plan_items: 'typing.List[JSONDict]' = []
300
300
  for item in raw_plan:
301
301
  if not isinstance(item, dict):
302
302
  continue
@@ -309,7 +309,7 @@ def extract_plan_items(arguments: object) -> list[JSONDict]:
309
309
  return plan_items
310
310
 
311
311
 
312
- def summarize_tool_event(call: ToolCall, result: ToolResult) -> str | None:
312
+ def summarize_tool_event(call: 'ToolCall', result: 'ToolResult') -> 'typing.Union[str, None]':
313
313
  command_preview = _command_preview(call)
314
314
  result_summary = _summarize_tool_result(result)
315
315
  if call.name == "update_plan":
@@ -322,8 +322,8 @@ def summarize_tool_event(call: ToolCall, result: ToolResult) -> str | None:
322
322
 
323
323
 
324
324
  def extract_tool_event_display(
325
- payload: dict[str, object],
326
- ) -> tuple[str, str, bool]:
325
+ payload: 'typing.Dict[str, object]',
326
+ ) -> 'typing.Tuple[str, str, bool]':
327
327
  tool_name = str(payload.get("tool_name", "")).strip()
328
328
  is_error = bool(payload.get("is_error"))
329
329
  call = payload.get("call")
@@ -334,7 +334,7 @@ def extract_tool_event_display(
334
334
  return tool_name, summary, is_error
335
335
 
336
336
 
337
- def extract_plan_event_items(payload: dict[str, object]) -> list[JSONDict]:
337
+ def extract_plan_event_items(payload: 'typing.Dict[str, object]') -> 'typing.List[JSONDict]':
338
338
  call = payload.get("call")
339
339
  if isinstance(call, ToolCall):
340
340
  return extract_plan_items(call.arguments)
@@ -344,14 +344,14 @@ def extract_plan_event_items(payload: dict[str, object]) -> list[JSONDict]:
344
344
  return []
345
345
 
346
346
 
347
- def _truncate_text(text: str, limit: int = 96) -> str:
347
+ def _truncate_text(text: 'str', limit: 'int' = 96) -> 'str':
348
348
  compact = " ".join(text.split())
349
349
  if len(compact) <= limit:
350
350
  return compact
351
351
  return compact[: limit - 3].rstrip() + "..."
352
352
 
353
353
 
354
- def _extract_output_preview(text: str) -> str | None:
354
+ def _extract_output_preview(text: 'str') -> 'typing.Union[str, None]':
355
355
  lines = [line.strip() for line in text.splitlines()]
356
356
  if "Output:" in lines:
357
357
  output_index = lines.index("Output:")
@@ -368,7 +368,7 @@ def _extract_output_preview(text: str) -> str | None:
368
368
  return None
369
369
 
370
370
 
371
- def _summarize_agent_status(status: object) -> str:
371
+ def _summarize_agent_status(status: 'object') -> 'str':
372
372
  if isinstance(status, str):
373
373
  return status
374
374
  if isinstance(status, dict):
@@ -382,7 +382,7 @@ def _summarize_agent_status(status: object) -> str:
382
382
  return _truncate_text(json.dumps(status, ensure_ascii=False, separators=(",", ":")))
383
383
 
384
384
 
385
- def _summarize_tool_result(result: ToolResult) -> str | None:
385
+ def _summarize_tool_result(result: 'ToolResult') -> 'typing.Union[str, None]':
386
386
  if result.name == "spawn_agent" and isinstance(result.output, dict):
387
387
  agent_id = str(result.output.get("agent_id", "")).strip()
388
388
  nickname = str(result.output.get("nickname", "")).strip()
@@ -399,7 +399,7 @@ def _summarize_tool_result(result: ToolResult) -> str | None:
399
399
  return "timed out"
400
400
  status = result.output.get("status")
401
401
  if isinstance(status, dict):
402
- parts: list[str] = []
402
+ parts: 'typing.List[str]' = []
403
403
  for agent_id, agent_status in status.items():
404
404
  if not isinstance(agent_id, str):
405
405
  continue
@@ -425,7 +425,7 @@ def _summarize_tool_result(result: ToolResult) -> str | None:
425
425
  return None
426
426
 
427
427
 
428
- def _string_arg(arguments: object, key: str) -> str | None:
428
+ def _string_arg(arguments: 'object', key: 'str') -> 'typing.Union[str, None]':
429
429
  if not isinstance(arguments, dict):
430
430
  return None
431
431
  value = arguments.get(key)
@@ -434,7 +434,7 @@ def _string_arg(arguments: object, key: str) -> str | None:
434
434
  return str(value)
435
435
 
436
436
 
437
- def _int_arg(arguments: object, key: str) -> int | None:
437
+ def _int_arg(arguments: 'object', key: 'str') -> 'typing.Union[int, None]':
438
438
  if not isinstance(arguments, dict):
439
439
  return None
440
440
  value = arguments.get(key)
@@ -443,7 +443,7 @@ def _int_arg(arguments: object, key: str) -> int | None:
443
443
  return int(value)
444
444
 
445
445
 
446
- def _command_preview(call: ToolCall) -> str | None:
446
+ def _command_preview(call: 'ToolCall') -> 'typing.Union[str, None]':
447
447
  if call.name == "exec_command":
448
448
  cmd = _string_arg(call.arguments, "cmd")
449
449
  if cmd:
@@ -503,7 +503,7 @@ def _command_preview(call: ToolCall) -> str | None:
503
503
  return None
504
504
 
505
505
 
506
- def _plan_progress_summary(plan: list[object]) -> str:
506
+ def _plan_progress_summary(plan: 'typing.List[object]') -> 'str':
507
507
  total = len(plan)
508
508
  completed = 0
509
509
  in_progress = 0
@@ -562,27 +562,27 @@ class CliSessionView:
562
562
  normal terminal stream so the reply is not lost.
563
563
  """
564
564
 
565
- def __init__(self) -> None:
565
+ def __init__(self) -> 'None':
566
566
  import sys
567
567
 
568
568
  self._line_output = print
569
569
  self._raw_write = sys.stdout.write
570
570
  self._raw_flush = sys.stdout.flush
571
571
  self._terminal_lock = threading.RLock()
572
- self._title: str | None = None
573
- self._pending_user_prompts: dict[str, str] = {}
574
- self._queued_steer_prompts: dict[str, list[str]] = {}
575
- self._inserted_steer_prompts: dict[str, list[str]] = {}
576
- self._history: list[tuple[str, str]] = []
572
+ self._title: 'typing.Union[str, None]' = None
573
+ self._pending_user_prompts: 'typing.Dict[str, str]' = {}
574
+ self._queued_steer_prompts: 'typing.Dict[str, typing.List[str]]' = {}
575
+ self._inserted_steer_prompts: 'typing.Dict[str, typing.List[str]]' = {}
576
+ self._history: 'typing.List[typing.Tuple[str, str]]' = []
577
577
  self._streaming = False
578
578
  self._prompt_stream_buffer = ""
579
579
  self._streaming_in_prompt = False
580
580
  self._input_active = False
581
581
  self._color_enabled = cli_color_enabled() and sys.stdout.isatty()
582
- self._agent_names: dict[str, str] = {}
583
- self._prompt_session: PromptSession | None = None
584
- self._prompt_task: asyncio.Task[str] | None = None
585
- self._stdout_proxy: StdoutProxy | None = None
582
+ self._agent_names: 'typing.Dict[str, str]' = {}
583
+ self._prompt_session: 'typing.Union[PromptSession, None]' = None
584
+ self._prompt_task: 'typing.Union[asyncio.Task[str], None]' = None
585
+ self._stdout_proxy: 'typing.Union[StdoutProxy, None]' = None
586
586
  self._spinner = Spinner(
587
587
  self._raw_write,
588
588
  self._raw_flush,
@@ -591,7 +591,7 @@ class CliSessionView:
591
591
  False,
592
592
  )
593
593
 
594
- def handle_event(self, event: AgentEvent) -> None:
594
+ def handle_event(self, event: 'AgentEvent') -> 'None':
595
595
  if event.kind == "turn_started":
596
596
  submission_id = str(event.payload.get("submission_id", event.turn_id)).strip()
597
597
  user_texts = event.payload.get("user_texts")
@@ -738,7 +738,7 @@ class CliSessionView:
738
738
  self._history.append((pending_prompt, final_text))
739
739
  return
740
740
 
741
- def show_history(self) -> None:
741
+ def show_history(self) -> 'None':
742
742
  self._finish_stream()
743
743
  if not self._history:
744
744
  self._print_line("No history yet.")
@@ -749,27 +749,27 @@ class CliSessionView:
749
749
  self._print_line(f"[{index}] user> {user_text}")
750
750
  self._print_line(f" assistant> {assistant_text}")
751
751
 
752
- def show_title(self) -> None:
752
+ def show_title(self) -> 'None':
753
753
  self._finish_stream()
754
754
  self._print_line(f"Session: {self._title or 'untitled'}")
755
755
 
756
- def pause_spinner(self) -> None:
756
+ def pause_spinner(self) -> 'None':
757
757
  self._spinner.pause()
758
758
 
759
- def resume_spinner(self) -> None:
759
+ def resume_spinner(self) -> 'None':
760
760
  self._spinner.resume()
761
761
 
762
- def set_input_active(self, active: bool, resume_spinner: bool = True) -> None:
762
+ def set_input_active(self, active: 'bool', resume_spinner: 'bool' = True) -> 'None':
763
763
  self._input_active = active
764
764
  if active:
765
765
  self._spinner.pause()
766
766
  elif resume_spinner:
767
767
  self._spinner.resume()
768
768
 
769
- def is_streaming_output(self) -> bool:
769
+ def is_streaming_output(self) -> 'bool':
770
770
  return self._streaming
771
771
 
772
- def handoff_prompt_stream_to_output(self) -> None:
772
+ def handoff_prompt_stream_to_output(self) -> 'None':
773
773
  if not self._streaming or not self._streaming_in_prompt:
774
774
  return
775
775
  buffered = self._prompt_stream_buffer
@@ -782,7 +782,7 @@ class CliSessionView:
782
782
  self._raw_write(buffered)
783
783
  self._raw_flush()
784
784
 
785
- async def poll_prompt(self, prompt: str) -> str | None:
785
+ async def poll_prompt(self, prompt: 'str') -> 'typing.Union[str, None]':
786
786
  if self._prompt_task is None:
787
787
  if self.is_streaming_output():
788
788
  return None
@@ -807,7 +807,7 @@ class CliSessionView:
807
807
  finally:
808
808
  self.set_input_active(False, resume_spinner=False)
809
809
 
810
- def build_input_prompt(self, prompt: str) -> str:
810
+ def build_input_prompt(self, prompt: 'str') -> 'str':
811
811
  if not self._input_active:
812
812
  return prompt
813
813
  if self._streaming and self._streaming_in_prompt:
@@ -819,7 +819,7 @@ class CliSessionView:
819
819
  return prompt
820
820
  return f"{prompt_line}\n{prompt}"
821
821
 
822
- def show_steer_queued(self, turn_id: str, prompt: str) -> None:
822
+ def show_steer_queued(self, turn_id: 'str', prompt: 'str') -> 'None':
823
823
  preview = shorten_title(prompt, limit=72)
824
824
  self._queued_steer_prompts.setdefault(turn_id, []).append(preview)
825
825
  self._print_line(
@@ -830,12 +830,12 @@ class CliSessionView:
830
830
  )
831
831
  )
832
832
 
833
- def schedule_steer_inserted(self, turn_id: str, prompt: str) -> None:
833
+ def schedule_steer_inserted(self, turn_id: 'str', prompt: 'str') -> 'None':
834
834
  self._inserted_steer_prompts.setdefault(turn_id, []).append(
835
835
  shorten_title(prompt, limit=72)
836
836
  )
837
837
 
838
- def close(self) -> None:
838
+ def close(self) -> 'None':
839
839
  if self._prompt_task is not None and not self._prompt_task.done():
840
840
  self._prompt_task.cancel()
841
841
  self._prompt_task = None
@@ -843,13 +843,13 @@ class CliSessionView:
843
843
  if self._stdout_proxy is not None:
844
844
  self._stdout_proxy.close()
845
845
 
846
- def finish_stream(self) -> None:
846
+ def finish_stream(self) -> 'None':
847
847
  self._finish_stream()
848
848
 
849
- def write_line(self, text: str) -> None:
849
+ def write_line(self, text: 'str') -> 'None':
850
850
  self._print_line(text)
851
851
 
852
- def show_error(self, text: str) -> None:
852
+ def show_error(self, text: 'str') -> 'None':
853
853
  self._spinner.finish_turn()
854
854
  self._finish_stream()
855
855
  self._print_line(
@@ -860,7 +860,7 @@ class CliSessionView:
860
860
  )
861
861
  )
862
862
 
863
- def _finish_stream(self) -> None:
863
+ def _finish_stream(self) -> 'None':
864
864
  with self._terminal_lock:
865
865
  self._spinner.clear()
866
866
  if self._streaming:
@@ -872,9 +872,9 @@ class CliSessionView:
872
872
 
873
873
  def _finalize_turn_output(
874
874
  self,
875
- final_text: str,
876
- allow_standalone_output: bool,
877
- ) -> None:
875
+ final_text: 'str',
876
+ allow_standalone_output: 'bool',
877
+ ) -> 'None':
878
878
  self._spinner.finish_turn()
879
879
  if self._streaming and self._streaming_in_prompt:
880
880
  streamed_text = self._prompt_stream_buffer
@@ -903,7 +903,7 @@ class CliSessionView:
903
903
  )
904
904
  )
905
905
 
906
- def _colorize_formatted_tool_message(self, message: str) -> str:
906
+ def _colorize_formatted_tool_message(self, message: 'str') -> 'str':
907
907
  if message.startswith("[plan]"):
908
908
  return colorize_cli_message(message, "plan", self._color_enabled)
909
909
  if message.startswith("[exec]"):
@@ -916,12 +916,12 @@ class CliSessionView:
916
916
  return colorize_cli_message(message, "error", self._color_enabled)
917
917
  return colorize_cli_message(message, "tool", self._color_enabled)
918
918
 
919
- def _print_line(self, text: str) -> None:
919
+ def _print_line(self, text: 'str') -> 'None':
920
920
  with self._terminal_lock:
921
921
  self._spinner.clear()
922
922
  self._line_output(text)
923
923
 
924
- def _remember_agent_name(self, tool_name: str, summary: str) -> None:
924
+ def _remember_agent_name(self, tool_name: 'str', summary: 'str') -> 'None':
925
925
  if tool_name != "spawn_agent":
926
926
  return
927
927
  if " (" not in summary or not summary.endswith(")"):
@@ -933,7 +933,7 @@ class CliSessionView:
933
933
  return
934
934
  self._agent_names[agent_short_id] = nickname
935
935
 
936
- def _rewrite_agent_summary(self, tool_name: str, summary: str) -> str:
936
+ def _rewrite_agent_summary(self, tool_name: 'str', summary: 'str') -> 'str':
937
937
  if tool_name not in {"wait_agent", "send_input", "resume_agent", "close_agent"}:
938
938
  return summary
939
939
  rewritten = summary
@@ -945,7 +945,7 @@ class CliSessionView:
945
945
  rewritten = rewritten.replace(agent_short_id, nickname)
946
946
  return rewritten
947
947
 
948
- async def prompt_async(self, prompt: str) -> str:
948
+ async def prompt_async(self, prompt: 'str') -> 'str':
949
949
  if self._prompt_session is None:
950
950
  self._prompt_session = PromptSession(
951
951
  erase_when_done=True,
@@ -966,7 +966,7 @@ class CliSessionView:
966
966
  finally:
967
967
  self.set_input_active(False, resume_spinner=False)
968
968
 
969
- async def _handoff_prompt_task_to_output(self) -> None:
969
+ async def _handoff_prompt_task_to_output(self) -> 'None':
970
970
  if self._prompt_task is None:
971
971
  return
972
972
  prompt_task = self._prompt_task
@@ -1,16 +1,22 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.1
2
2
  Name: python-codex
3
- Version: 0.1.1
3
+ Version: 0.1.3
4
4
  Summary: A minimal Python extraction of Codex's main agent loop
5
5
  License-File: LICENSE
6
- Requires-Python: >=3.10
7
- Requires-Dist: cryptography>=3.4
8
- Requires-Dist: fastapi>=0.115
6
+ Requires-Python: >=3.6.2
7
+ Requires-Dist: cryptography<41,>=40.0.2; python_version < '3.7'
8
+ Requires-Dist: cryptography>=40.0.2; python_version >= '3.7'
9
+ Requires-Dist: dataclasses>=0.8; python_version < '3.7'
10
+ Requires-Dist: fastapi<0.84,>=0.83.0; python_version < '3.7'
11
+ Requires-Dist: fastapi>=0.83.0; python_version >= '3.7'
12
+ Requires-Dist: importlib-metadata>=4.8.3; python_version < '3.8'
9
13
  Requires-Dist: loguru>=0.7.3
10
- Requires-Dist: prompt-toolkit>=3.0
11
- Requires-Dist: requests>=2.31
12
- Requires-Dist: tomli>=2.0; python_version < '3.11'
13
- Requires-Dist: uvicorn>=0.32
14
+ Requires-Dist: prompt-toolkit>=3.0.36
15
+ Requires-Dist: requests>=2.27.1
16
+ Requires-Dist: tomli<2,>=1.2.3; python_version < '3.11'
17
+ Requires-Dist: typing-extensions>=4.1.1; python_version < '3.8'
18
+ Requires-Dist: uvicorn<0.17,>=0.16.0; python_version < '3.7'
19
+ Requires-Dist: uvicorn>=0.16.0; python_version >= '3.7'
14
20
  Description-Content-Type: text/markdown
15
21
 
16
22
  # pycodex
@@ -0,0 +1,74 @@
1
+ pycodex/__init__.py,sha256=jCnC_Bgotlxa4GwO3Re2sChKGY49TRM-uVZEQ9uBpfw,3106
2
+ pycodex/agent.py,sha256=Qzt-P2uKVY93IxS6o_jU06quO18PY6NdvwZI0icu6Rw,10301
3
+ pycodex/cli.py,sha256=52YP6Njo7jnur3zasY1JhMbzDIYpXRDltcgpEuV_EzM,24993
4
+ pycodex/collaboration.py,sha256=yQ6pBD-R3ZWR4_FAYQFoS7KF0m4LLD42otXIbPqw2ys,641
5
+ pycodex/compat.py,sha256=IO0X7AgcYhlHnYnpvBZ6leCh_UjoQzg5HLT5wYBNNIw,3155
6
+ pycodex/context.py,sha256=3EVLjUZ39zu6avJehbOHm9JdghyJ6mZmySitiJmj6Jo,23895
7
+ pycodex/doctor.py,sha256=De3M4hRBJq8ZeqsUJgHz0vitqrH18YugrEnz7oHhTdQ,10572
8
+ pycodex/model.py,sha256=M0NtZ33s4DlajoqEWKEvWL_ijXkfp_Cbyzo3mTV3w1Q,20209
9
+ pycodex/portable.py,sha256=kZ5XVOMZq0l6xXsx3FY9C3DfB4Jra5Hw38qTMH0TEwg,15597
10
+ pycodex/portable_server.py,sha256=6I3pQkWj3e_SFlDXY2mGdCPns1w_3PSxByBV9wv5epI,7331
11
+ pycodex/protocol.py,sha256=LYDzJefu1tugqQzee4NuZzxhGAv3hXrNcnlw04CudAY,11106
12
+ pycodex/runtime.py,sha256=gpDDxQKfp1Cqh1U0uslI3rCoXEN7XpJGuHlV-bsveM0,7983
13
+ pycodex/runtime_services.py,sha256=OJjBWMjLAoc1cxLPKmz03XhMN5jOiBBNhxiKROytIbc,13118
14
+ pycodex/prompts/collaboration_default.md,sha256=MBTmPuMubeWfZgIeFVj49wwnwD4n_o3fVYAbgWKwu6Q,955
15
+ pycodex/prompts/collaboration_plan.md,sha256=IzjQAA5oHJz-3FmJdOjsJ4LHq6LW1tlEYMoy09n0HKk,8777
16
+ pycodex/prompts/default_base_instructions.md,sha256=D65mcj6bo4CDvVom-D9cbJRJVNquo0NghKt164_fRsg,20923
17
+ pycodex/prompts/exec_tools.json,sha256=2wYLsjL6VGzMnhFNCxE9IA_kxsxUspN68lr7JOlZq54,23369
18
+ pycodex/prompts/models.json,sha256=Xmuy5-FiiWdAe-Zz9w_-_kdEcRvIVssS1PugQSA64i8,251450
19
+ pycodex/prompts/subagent_tools.json,sha256=2ZOXyAiAaai2aazIlXdjjXb7cra5gZ2WYYbPltPaiYg,6199
20
+ pycodex/prompts/permissions/approval_policy/never.md,sha256=QceTG6wjkaJARjYr0HYV1aPnPcpGcrkRUW-smWRr6MQ,120
21
+ pycodex/prompts/permissions/approval_policy/on_failure.md,sha256=dfJjpXkpO6_ANdCKxbVJ8o4vyLxevrJWfKsGHTqtbkc,289
22
+ pycodex/prompts/permissions/approval_policy/on_request.md,sha256=hVQalzh0FAdkKzw5u-N4H7-LtC9ijVDlYsh3OKsZKzo,3661
23
+ pycodex/prompts/permissions/approval_policy/on_request_rule_request_permission.md,sha256=mOinishp1k-wlPsaEuIOMn5GoVm_dAIsWIuEMmv2r7o,1725
24
+ pycodex/prompts/permissions/approval_policy/unless_trusted.md,sha256=XHpi1Lfx1iIXFbbQ_ho_kGstA3JN-RLho291HM30UNw,247
25
+ pycodex/prompts/permissions/sandbox_mode/danger_full_access.md,sha256=nZ7YHacBd3cAHKRZc9XClOOOnXJPXPh0WFBueh5C2D0,197
26
+ pycodex/prompts/permissions/sandbox_mode/read_only.md,sha256=2rAPEXsBYCcuttI5j3euS-3uv_v97catIsnhxlSQSIM,173
27
+ pycodex/prompts/permissions/sandbox_mode/workspace_write.md,sha256=lVN-LwrBbHqlv5yVjcd_mU8tzZW8jfKpTatJKIZu9HI,277
28
+ pycodex/tools/__init__.py,sha256=aSLXrr_31KGQgDfRow5zVIc-2-KdXlHaCE6qUnE4HWI,1772
29
+ pycodex/tools/agent_tool_schemas.py,sha256=r7pBICcx8fb0Rg6IzIg8-u3um2z11TogQ4yCzuiO-4o,2033
30
+ pycodex/tools/apply_patch_tool.py,sha256=d-F9o3NsVjqwHqsR_hM7fYenBUQylfWr43JwMNroJkI,13985
31
+ pycodex/tools/base_tool.py,sha256=FLtbb6KPUKyhHRMrR6_anYi_GmpJFCaX1ch5aRnjQjo,5527
32
+ pycodex/tools/close_agent_tool.py,sha256=nY3l_UOX6NyTgUqdXag3yRpdyQScV0g0Vv4HE3ElLwg,1597
33
+ pycodex/tools/code_mode_manager.py,sha256=Wow42H_9IomUKUjjjU8rrAFAklhE-UlgxgrbgHRU_4M,19031
34
+ pycodex/tools/exec_command_tool.py,sha256=l8GWlZKTvlWWAd_OPKsnnt3m0woMWXK8NkilmspnaQQ,3485
35
+ pycodex/tools/exec_runtime.js,sha256=DR1uocKailTqNWAcJNFJuQgFFMSUzTpT_uQsRaneg2s,3643
36
+ pycodex/tools/exec_tool.py,sha256=pwzFCyjQMw2O9A6JDSTyOz6wmwg-UkFh_4wTY9rPLTI,1389
37
+ pycodex/tools/grep_files_tool.py,sha256=OiMKpM9vfaTpfEllQaI6Td39NnQ0gRDXqxMfmBwV2yQ,4797
38
+ pycodex/tools/list_dir_tool.py,sha256=qX4AGkmKkEt7qJaYTsaGIB0zCiyzcU5U8IBapAjEIQ0,4792
39
+ pycodex/tools/read_file_tool.py,sha256=xJk4f-lI-CJm50d5EThzWCz-7l2IX8s2gVwp-evsAJE,8148
40
+ pycodex/tools/request_permissions_tool.py,sha256=n0V0WvpaW3HBnBbdQoyi9CfL1dRBJDn7t9sWpg8wZ4g,3010
41
+ pycodex/tools/request_user_input_tool.py,sha256=mm3MbFIj6PYGgX4dv8MKSFCuiEFvHp23YGoJM2jv1ks,5734
42
+ pycodex/tools/resume_agent_tool.py,sha256=o62xdrsRxGFdRLxEhKEny-YEcaBOeqIneImrkME35II,1614
43
+ pycodex/tools/send_input_tool.py,sha256=P4VvZlNcHbfjhWF4vfEyHwDpyeyrhYJWLvvlWfvub2M,3608
44
+ pycodex/tools/shell_command_tool.py,sha256=hC2sg_ishmrqFIVyY5ngKnMOXjjIDL3xQ-gg40R1OQI,3520
45
+ pycodex/tools/shell_tool.py,sha256=7WaCWJM_-VeqyL5do_Qc8D-OZFWMXXY5Eo-ApSSsO9U,3698
46
+ pycodex/tools/spawn_agent_tool.py,sha256=YO5RLZpgv6uQPaEiCnOIGGBmGWREknUavvyAOFWWWeE,3563
47
+ pycodex/tools/unified_exec_manager.py,sha256=dSKv5fTSTWlSTktWyUXshGVijH9J74iuCYQxuse0oCY,13699
48
+ pycodex/tools/update_plan_tool.py,sha256=CiTTGAtG2b0EJyYsm2MgN_GXEKvCN8fvTgJGAcUCu-M,2884
49
+ pycodex/tools/view_image_tool.py,sha256=s3P1wPZioKrjjNXbwYEz9pzfVsnlPY79LzL-vujqRfY,3940
50
+ pycodex/tools/wait_agent_tool.py,sha256=0xjr5M2S0SNZaSr1o4U0RXI6dTJfMVpBB8Uclm_402I,2570
51
+ pycodex/tools/wait_tool.py,sha256=EJcW2Ev9jUD9eZ7cFDNOLDzlywS2BD3ll6pArXyxfrI,2331
52
+ pycodex/tools/web_search_tool.py,sha256=_7r2ltWhnBM0ZCgweA5a0GbEi0qSFAHOyi1RHrl6tfQ,957
53
+ pycodex/tools/write_stdin_tool.py,sha256=nCuProkbeewfQ_yS8CgBajo--K3EmkXzJYh1D2QtAM4,2549
54
+ pycodex/utils/__init__.py,sha256=Hj_0a7RhkAblWkaHyFhpi0cs2nSjJ1NdavbkBgEHieY,1024
55
+ pycodex/utils/dotenv.py,sha256=EDBXdn93ewmq9zhJki5_LsJJXe0wMIQJ6VfCE1r7voQ,1818
56
+ pycodex/utils/get_env.py,sha256=jR8G0Xco57jX-71E1oHIcl3-Kz9Ltc0kzxj04DKzt80,7316
57
+ pycodex/utils/random_ids.py,sha256=zBphjVGc7OXk9ZNExAbxRi_bk7ipyLG491qTv7hi8jM,380
58
+ pycodex/utils/visualize.py,sha256=528Mj0ubXS6UMabJxkTjduxYN9HsZFCImA70n3r1S8s,35904
59
+ responses_server/__init__.py,sha256=3yPv_zeGT7P11tTnmj5kXktISLNsNW-02MUnnbiZcb0,394
60
+ responses_server/__main__.py,sha256=9SRp-Yw7ShGxc6DhSIXcDLKgGEdAVm3oBZ59rBOPjT0,62
61
+ responses_server/app.py,sha256=ncGZN-MAJ5ExLWIErbFUqJIHprg2bZyRNjCJAStT7YI,7303
62
+ responses_server/config.py,sha256=wEcZbXZclTYz4fI_oy_sSMglWPeEITWlFeglQrrr6HE,2236
63
+ responses_server/payload_processors.py,sha256=y5aCpgpwkVuC6F5IQcBJEEFL-KMeQIHBqJzfmTRq1xM,3076
64
+ responses_server/server.py,sha256=isyzN-p-Ir8LLycN_dQfcanvie2ZqqSu52mOPz_wYD4,2095
65
+ responses_server/session_store.py,sha256=ZD3cH2aEOkWaQsu5qTzcal2mThTSFQPAhAhPUN9srgI,1115
66
+ responses_server/stream_router.py,sha256=bpafBCPhsJ81cFG9FfiQ6wTLn-OLkcV0ZLA_Kld6peM,30096
67
+ responses_server/tools/__init__.py,sha256=ivsBSEy0SBUhY-Uea5v1XMLXShkwHdCVl0id-1FwdZg,150
68
+ responses_server/tools/custom_adapter.py,sha256=LxO7ldydvR-GWachDz8GKC0Q8KGGFoFPbZxM0QvxuZ0,8350
69
+ responses_server/tools/web_search.py,sha256=pm4ZUiHUfxc0bGY1kEvt-BCzDrZIyP24xzPUcga2ul0,8908
70
+ python_codex-0.1.3.dist-info/METADATA,sha256=mMy9opmlVSpSQ4gX6SxJGNnBCijgqUGjV0BqWeNdF4c,14432
71
+ python_codex-0.1.3.dist-info/WHEEL,sha256=KGYbc1zXlYddvwxnNty23BeaKzh7YuoSIvIMO4jEhvw,87
72
+ python_codex-0.1.3.dist-info/entry_points.txt,sha256=sNUVakoVuTrzJH505ZgRTQxmtRRPUHV_EH0i6EbYTyM,45
73
+ python_codex-0.1.3.dist-info/licenses/LICENSE,sha256=0X8ifk312hYAORM4hlzg8wVSEXYKNmiPgWlB1YIy2Nw,10926
74
+ python_codex-0.1.3.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: hatchling 1.29.0
2
+ Generator: hatchling 1.17.1
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
@@ -0,0 +1,17 @@
1
+ from .app import (
2
+ ManagedResponseServer,
3
+ launch_chat_completion_compat_server,
4
+ run_server,
5
+ )
6
+ from .config import CompatServerConfig
7
+ from .server import ResponseServer
8
+ from .stream_router import StreamRouter
9
+
10
+ __all__ = [
11
+ "CompatServerConfig",
12
+ "ManagedResponseServer",
13
+ "ResponseServer",
14
+ "launch_chat_completion_compat_server",
15
+ "run_server",
16
+ "StreamRouter",
17
+ ]
@@ -0,0 +1,5 @@
1
+ from .app import main
2
+
3
+
4
+ if __name__ == "__main__":
5
+ main()