python-codex 0.1.4__tar.gz → 0.1.5__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 (101) hide show
  1. {python_codex-0.1.4 → python_codex-0.1.5}/PKG-INFO +1 -1
  2. {python_codex-0.1.4 → python_codex-0.1.5}/pycodex/utils/visualize.py +2 -2
  3. {python_codex-0.1.4 → python_codex-0.1.5}/pyproject.toml +1 -1
  4. {python_codex-0.1.4 → python_codex-0.1.5}/responses_server/app.py +2 -1
  5. {python_codex-0.1.4 → python_codex-0.1.5}/tests/responses_server/test_server.py +46 -1
  6. {python_codex-0.1.4 → python_codex-0.1.5}/.github/workflows/publish.yml +0 -0
  7. {python_codex-0.1.4 → python_codex-0.1.5}/.github/workflows/test.yml +0 -0
  8. {python_codex-0.1.4 → python_codex-0.1.5}/.gitignore +0 -0
  9. {python_codex-0.1.4 → python_codex-0.1.5}/AGENTS.md +0 -0
  10. {python_codex-0.1.4 → python_codex-0.1.5}/LICENSE +0 -0
  11. {python_codex-0.1.4 → python_codex-0.1.5}/README.md +0 -0
  12. {python_codex-0.1.4 → python_codex-0.1.5}/README_ZH.md +0 -0
  13. {python_codex-0.1.4 → python_codex-0.1.5}/docs/ALIGNMENT.md +0 -0
  14. {python_codex-0.1.4 → python_codex-0.1.5}/docs/CONTEXT.md +0 -0
  15. {python_codex-0.1.4 → python_codex-0.1.5}/docs/responses_server/README.md +0 -0
  16. {python_codex-0.1.4 → python_codex-0.1.5}/pycodex/__init__.py +0 -0
  17. {python_codex-0.1.4 → python_codex-0.1.5}/pycodex/agent.py +0 -0
  18. {python_codex-0.1.4 → python_codex-0.1.5}/pycodex/cli.py +0 -0
  19. {python_codex-0.1.4 → python_codex-0.1.5}/pycodex/collaboration.py +0 -0
  20. {python_codex-0.1.4 → python_codex-0.1.5}/pycodex/compat.py +0 -0
  21. {python_codex-0.1.4 → python_codex-0.1.5}/pycodex/context.py +0 -0
  22. {python_codex-0.1.4 → python_codex-0.1.5}/pycodex/doctor.py +0 -0
  23. {python_codex-0.1.4 → python_codex-0.1.5}/pycodex/model.py +0 -0
  24. {python_codex-0.1.4 → python_codex-0.1.5}/pycodex/portable.py +0 -0
  25. {python_codex-0.1.4 → python_codex-0.1.5}/pycodex/portable_server.py +0 -0
  26. {python_codex-0.1.4 → python_codex-0.1.5}/pycodex/prompts/collaboration_default.md +0 -0
  27. {python_codex-0.1.4 → python_codex-0.1.5}/pycodex/prompts/collaboration_plan.md +0 -0
  28. {python_codex-0.1.4 → python_codex-0.1.5}/pycodex/prompts/default_base_instructions.md +0 -0
  29. {python_codex-0.1.4 → python_codex-0.1.5}/pycodex/prompts/exec_tools.json +0 -0
  30. {python_codex-0.1.4 → python_codex-0.1.5}/pycodex/prompts/models.json +0 -0
  31. {python_codex-0.1.4 → python_codex-0.1.5}/pycodex/prompts/permissions/approval_policy/never.md +0 -0
  32. {python_codex-0.1.4 → python_codex-0.1.5}/pycodex/prompts/permissions/approval_policy/on_failure.md +0 -0
  33. {python_codex-0.1.4 → python_codex-0.1.5}/pycodex/prompts/permissions/approval_policy/on_request.md +0 -0
  34. {python_codex-0.1.4 → python_codex-0.1.5}/pycodex/prompts/permissions/approval_policy/on_request_rule_request_permission.md +0 -0
  35. {python_codex-0.1.4 → python_codex-0.1.5}/pycodex/prompts/permissions/approval_policy/unless_trusted.md +0 -0
  36. {python_codex-0.1.4 → python_codex-0.1.5}/pycodex/prompts/permissions/sandbox_mode/danger_full_access.md +0 -0
  37. {python_codex-0.1.4 → python_codex-0.1.5}/pycodex/prompts/permissions/sandbox_mode/read_only.md +0 -0
  38. {python_codex-0.1.4 → python_codex-0.1.5}/pycodex/prompts/permissions/sandbox_mode/workspace_write.md +0 -0
  39. {python_codex-0.1.4 → python_codex-0.1.5}/pycodex/prompts/subagent_tools.json +0 -0
  40. {python_codex-0.1.4 → python_codex-0.1.5}/pycodex/protocol.py +0 -0
  41. {python_codex-0.1.4 → python_codex-0.1.5}/pycodex/runtime.py +0 -0
  42. {python_codex-0.1.4 → python_codex-0.1.5}/pycodex/runtime_services.py +0 -0
  43. {python_codex-0.1.4 → python_codex-0.1.5}/pycodex/tools/__init__.py +0 -0
  44. {python_codex-0.1.4 → python_codex-0.1.5}/pycodex/tools/agent_tool_schemas.py +0 -0
  45. {python_codex-0.1.4 → python_codex-0.1.5}/pycodex/tools/apply_patch_tool.py +0 -0
  46. {python_codex-0.1.4 → python_codex-0.1.5}/pycodex/tools/base_tool.py +0 -0
  47. {python_codex-0.1.4 → python_codex-0.1.5}/pycodex/tools/close_agent_tool.py +0 -0
  48. {python_codex-0.1.4 → python_codex-0.1.5}/pycodex/tools/code_mode_manager.py +0 -0
  49. {python_codex-0.1.4 → python_codex-0.1.5}/pycodex/tools/exec_command_tool.py +0 -0
  50. {python_codex-0.1.4 → python_codex-0.1.5}/pycodex/tools/exec_runtime.js +0 -0
  51. {python_codex-0.1.4 → python_codex-0.1.5}/pycodex/tools/exec_tool.py +0 -0
  52. {python_codex-0.1.4 → python_codex-0.1.5}/pycodex/tools/grep_files_tool.py +0 -0
  53. {python_codex-0.1.4 → python_codex-0.1.5}/pycodex/tools/list_dir_tool.py +0 -0
  54. {python_codex-0.1.4 → python_codex-0.1.5}/pycodex/tools/read_file_tool.py +0 -0
  55. {python_codex-0.1.4 → python_codex-0.1.5}/pycodex/tools/request_permissions_tool.py +0 -0
  56. {python_codex-0.1.4 → python_codex-0.1.5}/pycodex/tools/request_user_input_tool.py +0 -0
  57. {python_codex-0.1.4 → python_codex-0.1.5}/pycodex/tools/resume_agent_tool.py +0 -0
  58. {python_codex-0.1.4 → python_codex-0.1.5}/pycodex/tools/send_input_tool.py +0 -0
  59. {python_codex-0.1.4 → python_codex-0.1.5}/pycodex/tools/shell_command_tool.py +0 -0
  60. {python_codex-0.1.4 → python_codex-0.1.5}/pycodex/tools/shell_tool.py +0 -0
  61. {python_codex-0.1.4 → python_codex-0.1.5}/pycodex/tools/spawn_agent_tool.py +0 -0
  62. {python_codex-0.1.4 → python_codex-0.1.5}/pycodex/tools/unified_exec_manager.py +0 -0
  63. {python_codex-0.1.4 → python_codex-0.1.5}/pycodex/tools/update_plan_tool.py +0 -0
  64. {python_codex-0.1.4 → python_codex-0.1.5}/pycodex/tools/view_image_tool.py +0 -0
  65. {python_codex-0.1.4 → python_codex-0.1.5}/pycodex/tools/wait_agent_tool.py +0 -0
  66. {python_codex-0.1.4 → python_codex-0.1.5}/pycodex/tools/wait_tool.py +0 -0
  67. {python_codex-0.1.4 → python_codex-0.1.5}/pycodex/tools/web_search_tool.py +0 -0
  68. {python_codex-0.1.4 → python_codex-0.1.5}/pycodex/tools/write_stdin_tool.py +0 -0
  69. {python_codex-0.1.4 → python_codex-0.1.5}/pycodex/utils/__init__.py +0 -0
  70. {python_codex-0.1.4 → python_codex-0.1.5}/pycodex/utils/compactor.py +0 -0
  71. {python_codex-0.1.4 → python_codex-0.1.5}/pycodex/utils/dotenv.py +0 -0
  72. {python_codex-0.1.4 → python_codex-0.1.5}/pycodex/utils/get_env.py +0 -0
  73. {python_codex-0.1.4 → python_codex-0.1.5}/pycodex/utils/random_ids.py +0 -0
  74. {python_codex-0.1.4 → python_codex-0.1.5}/pycodex/utils/session_persist.py +0 -0
  75. {python_codex-0.1.4 → python_codex-0.1.5}/responses_server/__init__.py +0 -0
  76. {python_codex-0.1.4 → python_codex-0.1.5}/responses_server/__main__.py +0 -0
  77. {python_codex-0.1.4 → python_codex-0.1.5}/responses_server/config.py +0 -0
  78. {python_codex-0.1.4 → python_codex-0.1.5}/responses_server/payload_processors.py +0 -0
  79. {python_codex-0.1.4 → python_codex-0.1.5}/responses_server/server.py +0 -0
  80. {python_codex-0.1.4 → python_codex-0.1.5}/responses_server/session_store.py +0 -0
  81. {python_codex-0.1.4 → python_codex-0.1.5}/responses_server/stream_router.py +0 -0
  82. {python_codex-0.1.4 → python_codex-0.1.5}/responses_server/tools/__init__.py +0 -0
  83. {python_codex-0.1.4 → python_codex-0.1.5}/responses_server/tools/custom_adapter.py +0 -0
  84. {python_codex-0.1.4 → python_codex-0.1.5}/responses_server/tools/web_search.py +0 -0
  85. {python_codex-0.1.4 → python_codex-0.1.5}/tests/TESTS.md +0 -0
  86. {python_codex-0.1.4 → python_codex-0.1.5}/tests/__init__.py +0 -0
  87. {python_codex-0.1.4 → python_codex-0.1.5}/tests/compare_request_user_input_roundtrip.py +0 -0
  88. {python_codex-0.1.4 → python_codex-0.1.5}/tests/compare_steer_request_bodies.py +0 -0
  89. {python_codex-0.1.4 → python_codex-0.1.5}/tests/compare_tool_schemas.py +0 -0
  90. {python_codex-0.1.4 → python_codex-0.1.5}/tests/fake_responses_server.py +0 -0
  91. {python_codex-0.1.4 → python_codex-0.1.5}/tests/fakes.py +0 -0
  92. {python_codex-0.1.4 → python_codex-0.1.5}/tests/responses_server/fake_chat_completions_server.py +0 -0
  93. {python_codex-0.1.4 → python_codex-0.1.5}/tests/test_agent.py +0 -0
  94. {python_codex-0.1.4 → python_codex-0.1.5}/tests/test_builtin_tools.py +0 -0
  95. {python_codex-0.1.4 → python_codex-0.1.5}/tests/test_cli.py +0 -0
  96. {python_codex-0.1.4 → python_codex-0.1.5}/tests/test_compactor.py +0 -0
  97. {python_codex-0.1.4 → python_codex-0.1.5}/tests/test_context.py +0 -0
  98. {python_codex-0.1.4 → python_codex-0.1.5}/tests/test_doctor.py +0 -0
  99. {python_codex-0.1.4 → python_codex-0.1.5}/tests/test_fake_responses_server.py +0 -0
  100. {python_codex-0.1.4 → python_codex-0.1.5}/tests/test_model.py +0 -0
  101. {python_codex-0.1.4 → python_codex-0.1.5}/tests/test_portable.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: python-codex
3
- Version: 0.1.4
3
+ Version: 0.1.5
4
4
  Summary: A minimal Python extraction of Codex's main agent loop
5
5
  License-File: LICENSE
6
6
  Requires-Python: >=3.6.2
@@ -83,7 +83,7 @@ def format_cli_plan_messages(
83
83
 
84
84
  def build_cli_spinner_frame(index: 'int', label: 'str') -> 'str':
85
85
  suffix = f" {label}" if label else ""
86
- return f"{suffix} {SPINNER_FRAMES[index % len(SPINNER_FRAMES)]}"
86
+ return f"{SPINNER_FRAMES[index % len(SPINNER_FRAMES)]}{suffix}"
87
87
 
88
88
 
89
89
  def percent_of_context_window_remaining(
@@ -740,7 +740,7 @@ class CliSessionView:
740
740
  else:
741
741
  self._spinner.resume()
742
742
  if tool_name and args is not None:
743
- self._spinner.set_label(f"running {tool_name}({args})")
743
+ self._spinner.set_label(shorten_title(f"running {tool_name}({args})", limit=72))
744
744
  elif tool_name:
745
745
  self._spinner.set_label(f"running {tool_name}")
746
746
  else:
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "python-codex"
7
- version = "0.1.4"
7
+ version = "0.1.5"
8
8
  description = "A minimal Python extraction of Codex's main agent loop"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.6.2"
@@ -36,13 +36,14 @@ def _stream_events(response_server: 'ResponseServer', request_body: 'typing.Dict
36
36
  except OutcommingChatError as exc:
37
37
 
38
38
  import traceback
39
+ exc_info = traceback.format_exception(type(exc), exc, exc.__traceback__)
39
40
  yield _format_sse_event(
40
41
  "response.failed",
41
42
  {
42
43
  "type": "response.failed",
43
44
  "response": {
44
45
  "error": {
45
- "message": '\n'.join(traceback.format_exception(exc)),
46
+ "message": '\n'.join(exc_info),
46
47
  }
47
48
  },
48
49
  },
@@ -1396,6 +1396,7 @@ def test_responses_server_turns_truncated_downstream_stream_into_response_failed
1396
1396
  def serve_truncated_stream(listener: 'socket.socket') -> 'None':
1397
1397
  conn, _addr = listener.accept()
1398
1398
  try:
1399
+ # Read request headers (up to the empty line)
1399
1400
  request_bytes = b""
1400
1401
  while b"\r\n\r\n" not in request_bytes:
1401
1402
  chunk = conn.recv(65536)
@@ -1403,6 +1404,38 @@ def test_responses_server_turns_truncated_downstream_stream_into_response_failed
1403
1404
  break
1404
1405
  request_bytes += chunk
1405
1406
 
1407
+ # Separate headers and any body bytes already read
1408
+ sep_idx = request_bytes.find(b"\r\n\r\n")
1409
+ if sep_idx == -1:
1410
+ header_bytes = request_bytes
1411
+ body_so_far = b""
1412
+ else:
1413
+ header_bytes = request_bytes[:sep_idx]
1414
+ body_so_far = request_bytes[sep_idx + 4:]
1415
+
1416
+ # Parse Content-Length to consume the exact request body
1417
+ content_length = 0
1418
+ try:
1419
+ header_text = header_bytes.decode("latin-1")
1420
+ for line in header_text.split("\r\n"):
1421
+ if ":" in line:
1422
+ key, value = line.split(":", 1)
1423
+ if key.strip().lower() == "content-length":
1424
+ content_length = int(value.strip())
1425
+ break
1426
+ except Exception:
1427
+ content_length = 0
1428
+
1429
+ # Already read some body bytes? Consume the rest if needed
1430
+ already = len(body_so_far)
1431
+ remaining = content_length - already
1432
+ if remaining > 0:
1433
+ while remaining > 0:
1434
+ chunk = conn.recv(min(65536, remaining))
1435
+ if not chunk:
1436
+ break
1437
+ remaining -= len(chunk)
1438
+
1406
1439
  body = (
1407
1440
  b'data: {"id":"chatcmpl_mock","object":"chat.completion.chunk",'
1408
1441
  b'"model":"gpt-5.4","choices":[{"index":0,"delta":{"content":"hi"},'
@@ -1417,6 +1450,9 @@ def test_responses_server_turns_truncated_downstream_stream_into_response_failed
1417
1450
  conn.sendall(f"{len(body):X}\r\n".encode("ascii"))
1418
1451
  conn.sendall(body)
1419
1452
  conn.sendall(b"\r\n")
1453
+ # Proper chunked termination so clients on older Python (e.g. 3.8)
1454
+ # don't raise ChunkedEncodingError before we can read the partial body.
1455
+ conn.sendall(b"0\r\n\r\n")
1420
1456
  finally:
1421
1457
  conn.close()
1422
1458
  listener.close()
@@ -1458,7 +1494,16 @@ def test_responses_server_turns_truncated_downstream_stream_into_response_failed
1458
1494
  )
1459
1495
  with response:
1460
1496
  status = response.status_code
1461
- body = response.text
1497
+ # Collect SSE lines manually to avoid ChunkedEncodingError on Python 3.8
1498
+ # when the downstream stream is truncated. requests may raise after we've
1499
+ # already consumed some chunks, so we read iter_lines() and stop on error.
1500
+ lines = []
1501
+ try:
1502
+ for line in response.iter_lines(decode_unicode=True):
1503
+ lines.append(line)
1504
+ except requests.exceptions.ChunkedEncodingError:
1505
+ pass # Expected on truncated stream, partial lines already collected
1506
+ body = "\n".join(lines)
1462
1507
  finally:
1463
1508
  server.stop()
1464
1509
  downstream_thread.join(timeout=5)
File without changes
File without changes
File without changes
File without changes
File without changes