regcode 0.11.0__tar.gz → 0.12.1__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.
- {regcode-0.11.0 → regcode-0.12.1}/GOALS.md +114 -0
- regcode-0.11.0/README.md → regcode-0.12.1/PKG-INFO +41 -0
- regcode-0.11.0/PKG-INFO → regcode-0.12.1/README.md +18 -22
- {regcode-0.11.0 → regcode-0.12.1}/pyproject.toml +2 -1
- {regcode-0.11.0 → regcode-0.12.1}/regcode/__init__.py +1 -1
- regcode-0.12.1/regcode/agents_backend.py +811 -0
- regcode-0.12.1/regcode/async_utils.py +58 -0
- {regcode-0.11.0 → regcode-0.12.1}/regcode/cli.py +12 -1
- {regcode-0.11.0 → regcode-0.12.1}/regcode/main.py +423 -633
- {regcode-0.11.0 → regcode-0.12.1}/regcode/monty_sandbox.py +13 -11
- {regcode-0.11.0 → regcode-0.12.1}/regcode/tools/builtins.py +61 -0
- {regcode-0.11.0 → regcode-0.12.1}/regcode/tools/web_crawl.py +6 -3
- regcode-0.12.1/tasks.md +632 -0
- {regcode-0.11.0 → regcode-0.12.1}/tests/conftest.py +2 -1
- regcode-0.12.1/tests/llm_fakes.py +187 -0
- {regcode-0.11.0 → regcode-0.12.1}/tests/test_clipboard.py +18 -13
- {regcode-0.11.0 → regcode-0.12.1}/tests/test_dependencies.py +27 -0
- {regcode-0.11.0 → regcode-0.12.1}/tests/test_main.py +346 -383
- {regcode-0.11.0 → regcode-0.12.1}/tests/test_provider_extra.py +21 -29
- regcode-0.12.1/tests/test_task_regression.py +755 -0
- {regcode-0.11.0 → regcode-0.12.1}/uv.lock +184 -39
- {regcode-0.11.0 → regcode-0.12.1}/.gitignore +0 -0
- {regcode-0.11.0 → regcode-0.12.1}/.gitlab-ci.yml +0 -0
- {regcode-0.11.0 → regcode-0.12.1}/.python-version +0 -0
- {regcode-0.11.0 → regcode-0.12.1}/.qwen/settings.json +0 -0
- {regcode-0.11.0 → regcode-0.12.1}/AGENTS.md +0 -0
- {regcode-0.11.0 → regcode-0.12.1}/config.yaml.template +0 -0
- {regcode-0.11.0 → regcode-0.12.1}/main.py +0 -0
- {regcode-0.11.0 → regcode-0.12.1}/regcode/clipboard.py +0 -0
- {regcode-0.11.0 → regcode-0.12.1}/regcode/config.py +0 -0
- {regcode-0.11.0 → regcode-0.12.1}/regcode/conversation_manager.py +0 -0
- {regcode-0.11.0 → regcode-0.12.1}/regcode/dependencies.py +0 -0
- {regcode-0.11.0 → regcode-0.12.1}/regcode/mcp_client.py +0 -0
- {regcode-0.11.0 → regcode-0.12.1}/regcode/paste.py +0 -0
- {regcode-0.11.0 → regcode-0.12.1}/regcode/permissions.py +0 -0
- {regcode-0.11.0 → regcode-0.12.1}/regcode/sandbox.py +0 -0
- {regcode-0.11.0 → regcode-0.12.1}/regcode/tools/__init__.py +0 -0
- {regcode-0.11.0 → regcode-0.12.1}/regcode/tools/base.py +0 -0
- {regcode-0.11.0 → regcode-0.12.1}/regcode/tools/registry.py +0 -0
- {regcode-0.11.0 → regcode-0.12.1}/regcode/tools/review_notes.py +0 -0
- {regcode-0.11.0 → regcode-0.12.1}/regcode/tools/web_search.py +0 -0
- {regcode-0.11.0 → regcode-0.12.1}/regcode/tracing.py +0 -0
- {regcode-0.11.0 → regcode-0.12.1}/regcode/tui.py +0 -0
- {regcode-0.11.0 → regcode-0.12.1}/tests/__init__.py +0 -0
- {regcode-0.11.0 → regcode-0.12.1}/tests/test_api.py +0 -0
- {regcode-0.11.0 → regcode-0.12.1}/tests/test_cli.py +0 -0
- {regcode-0.11.0 → regcode-0.12.1}/tests/test_config.py +0 -0
- {regcode-0.11.0 → regcode-0.12.1}/tests/test_context_manager.py +0 -0
- {regcode-0.11.0 → regcode-0.12.1}/tests/test_mcp.py +0 -0
- {regcode-0.11.0 → regcode-0.12.1}/tests/test_mcp_status.py +0 -0
- {regcode-0.11.0 → regcode-0.12.1}/tests/test_monty_sandbox.py +0 -0
- {regcode-0.11.0 → regcode-0.12.1}/tests/test_osv_cve.py +0 -0
- {regcode-0.11.0 → regcode-0.12.1}/tests/test_review_notes.py +0 -0
- {regcode-0.11.0 → regcode-0.12.1}/tests/test_web_search.py +0 -0
|
@@ -581,3 +581,117 @@ mcp_servers:
|
|
|
581
581
|
2. ✅ **TUI integration** — `_run_agent_sync()` and `_run_agent_multimodal()` now pass `on_message` callback to `agent.chat()`. The callback inserts each assistant text response directly into the chat area via `call_from_thread(self._insert_rich, text)`, so the user sees intermediate thinking ("Let me search the internet...") BEFORE tool execution markers appear. This makes the agent feel alive and responsive instead of silent during multi-tool chains.
|
|
582
582
|
|
|
583
583
|
3. ✅ **All 193 tests pass, lint clean.**
|
|
584
|
+
|
|
585
|
+
## Completed (tasks.md — upstream fixes for GitKnecht)
|
|
586
|
+
|
|
587
|
+
All twelve defects in `tasks.md` are fixed. See the ticked **Task Status** section there
|
|
588
|
+
for the per-task mapping to tests.
|
|
589
|
+
|
|
590
|
+
1. ✅ **Deleted the two dead tool loops** — the real cause of most of the list was that
|
|
591
|
+
`Agent` carried three tool loops (`_stream`, `_chat_loop`, and the SDK path) and only
|
|
592
|
+
the SDK one was reachable from `chat()`. Tasks 1/2/8/9/12 were all symptoms of the same
|
|
593
|
+
unreachable, never-executed `_stream`. `_stream`, `_chat_loop`,
|
|
594
|
+
`Agent._execute_tool_calls` and `_convert_registry_tools_to_sdk_tools` are deleted;
|
|
595
|
+
`test_module_level_helpers_exist` pins their absence.
|
|
596
|
+
|
|
597
|
+
2. ✅ **One implementation for both modes** — `Agent.chat()` delegates to `_run_sdk()`,
|
|
598
|
+
which drives the OpenAI Agents SDK `Runner` in either `run()` or `run_streamed()` form.
|
|
599
|
+
`RegCodeModel` (`regcode/agents_backend.py`) is now a **pure model**: one completion in,
|
|
600
|
+
output items out, exactly like the SDK's own `LitellmModel`. The Runner owns the tool
|
|
601
|
+
loop, so `max_tool_calls` (as `max_turns`), `response_format`, the budget and the
|
|
602
|
+
callbacks behave identically streaming or not.
|
|
603
|
+
|
|
604
|
+
3. ✅ **Streaming actually works** — `stream_response` uses `await litellm.acompletion(
|
|
605
|
+
stream=True)` and hands the async iterator to `ChatCmplStreamHandler` with an empty
|
|
606
|
+
`Response` shell. It previously called the *sync* `completion(stream=True)` and passed
|
|
607
|
+
the result in twice, so `chat(stream=True)` raised
|
|
608
|
+
`TypeError: 'async for' requires an object with __aiter__ method, got ModelResponse`.
|
|
609
|
+
|
|
610
|
+
4. ✅ `status_callback`/`result_callback` **never fired** — the model loop ignored them
|
|
611
|
+
entirely. They now fire from `_run_tool`, the single place tools execute, with
|
|
612
|
+
`is_error` read from `ToolResult.error`.
|
|
613
|
+
|
|
614
|
+
5. ✅ **`FunctionTool` wrapper fixed** — it read `ctx.tool_registry` (a
|
|
615
|
+
`RunContextWrapper` has no such attribute) and closed over the loop variable, so every
|
|
616
|
+
tool would have invoked the *last* registered one. The name is now bound via a default
|
|
617
|
+
argument.
|
|
618
|
+
|
|
619
|
+
6. ✅ **`directory` is now enforced** — `validate_tool_args` rejects any
|
|
620
|
+
`path`/`file_path`/`script_path`/`directory` argument resolving outside the root;
|
|
621
|
+
`shell_command` runs with `cwd=directory` and rejects absolute arguments outside it;
|
|
622
|
+
the CLI gained `--directory` (previously the agent was constructed with no directory at
|
|
623
|
+
all, so the default toolset was unconfined). README documents that `directory` is
|
|
624
|
+
confinement, not a sandbox.
|
|
625
|
+
|
|
626
|
+
7. ✅ **`AGENTS.md` no longer leaks from the process cwd** — `include_cwd` defaults to
|
|
627
|
+
`False`, closing a prompt-injection surface where the host project's instructions were
|
|
628
|
+
injected into answers about an unrelated repository.
|
|
629
|
+
|
|
630
|
+
8. ✅ **Per-iteration compaction restored** — `call_model_input_filter` on `RunConfig`
|
|
631
|
+
re-checks the token budget on every Runner iteration, which the SDK had silently
|
|
632
|
+
dropped along with the old loops.
|
|
633
|
+
|
|
634
|
+
9. ✅ **`MaxTurnsExceeded` is handled, not fatal** — hitting the turn limit is an
|
|
635
|
+
expected outcome for long reviews, so the best text produced so far is recovered from
|
|
636
|
+
`exc.run_data` instead of crashing the caller.
|
|
637
|
+
|
|
638
|
+
10. ✅ **Test fidelity** — `tests/llm_fakes.py` builds **real** `litellm.types.utils`
|
|
639
|
+
objects instead of `MagicMock`. The streaming bug survived for releases because a
|
|
640
|
+
`MagicMock` answered whatever the test asked it to; a response shape the SDK cannot
|
|
641
|
+
parse now fails the test the way it fails in production. Also fixed
|
|
642
|
+
`tests/test_dependencies.py`, which deleted `regcode.*` from `sys.modules` without
|
|
643
|
+
restoring it, so later tests received a second `AgentStatus` class and enum identity
|
|
644
|
+
comparisons failed purely on execution order.
|
|
645
|
+
|
|
646
|
+
11. ✅ **285 tests pass** (up from 231), `uv run ruff check` clean.
|
|
647
|
+
|
|
648
|
+
## Fixed (2026-10-31 — CLI broke on the first real conversation)
|
|
649
|
+
|
|
650
|
+
The rewrite passed every test but broke `regcode chat` as soon as the model said
|
|
651
|
+
anything before calling a tool:
|
|
652
|
+
|
|
653
|
+
```
|
|
654
|
+
[ERROR] Agent error! litellm.BadRequestError: Hosted_vllmException - {"error":{"message":
|
|
655
|
+
"3 validation errors for ValidatorIterator\n0.ChatCompletionContentPartTextParam.type\n
|
|
656
|
+
Input should be 'text' [type=literal_error, input_value='output_text'...
|
|
657
|
+
```
|
|
658
|
+
|
|
659
|
+
Three separate regressions, all invisible to the existing suite because **every test
|
|
660
|
+
made the model return *either* text *or* a tool call** — so the second turn, the one
|
|
661
|
+
where the Runner replays the assistant's own output, never executed.
|
|
662
|
+
|
|
663
|
+
1. ✅ **Responses-API items were leaking into chat-completions requests** — the Runner
|
|
664
|
+
hands the assistant's output back as `{"type": "message", "content": [{"type":
|
|
665
|
+
"output_text", ...}]}`, `function_call` and `function_call_output`. `_prepare_messages`
|
|
666
|
+
copied those dicts straight into `litellm.completion(messages=...)`, which the provider
|
|
667
|
+
rejects. It now runs them through `Converter.items_to_messages(items, model=...)`, the
|
|
668
|
+
same translation the SDK's own `LitellmModel` uses — which also merges an assistant's
|
|
669
|
+
text with the tool calls it asked for into a single turn.
|
|
670
|
+
|
|
671
|
+
2. ✅ **Replayed history was structurally invalid** — `_update_history_from_sdk_result`
|
|
672
|
+
stored a `"Tool call: x"` label next to a `role: "tool"` result. A tool result is only
|
|
673
|
+
legal when an assistant turn carrying the matching `tool_calls` precedes it, so the
|
|
674
|
+
second `chat()` call would have been rejected by strict providers (vLLM among them).
|
|
675
|
+
History now stores the real `tool_calls` array (id, name, arguments). `ToolCallItem.raw_item`
|
|
676
|
+
is a `ResponseFunctionToolCall` **object** when the Runner builds it and a dict elsewhere;
|
|
677
|
+
reading it only as a dict silently produced `arguments: "{}"`, so a replayed turn claimed
|
|
678
|
+
the model had called a tool with no arguments. `_item_field()` handles both.
|
|
679
|
+
|
|
680
|
+
3. ✅ **`python_code`, `run_script` and `web_crawl` were dead** — moving tool execution
|
|
681
|
+
under the Runner put them *inside* an event loop, where `asyncio.run()` raises
|
|
682
|
+
`cannot be called from a running event loop`. Every `python_code` call returned
|
|
683
|
+
`SANDBOX_ERROR: ...` and the model just got an error string back. Added
|
|
684
|
+
`regcode/async_utils.py::run_coroutine_blocking()`, which runs the coroutine on a
|
|
685
|
+
dedicated thread when a loop is already active.
|
|
686
|
+
|
|
687
|
+
Also: `chat(messages=...)` is public API (`code_review`, GitKnecht, saved transcripts)
|
|
688
|
+
and accepts plain chat-shaped dicts, which `Converter.items_to_messages` rejects outright.
|
|
689
|
+
`_expand_input_items()` normalises `role: "tool"` and assistant `tool_calls` into Responses
|
|
690
|
+
items before conversion.
|
|
691
|
+
|
|
692
|
+
**Process lesson**: the new tests were written against the shapes the *tests* invented.
|
|
693
|
+
`tests/test_task_regression.py` now pins the message shape of every turn handed to litellm
|
|
694
|
+
(`_assert_chat_completions_shape`), covers text-plus-tool-call turns, a second `chat()`
|
|
695
|
+
call, and tool execution under a running loop. Each was mutation-checked — revert the fix,
|
|
696
|
+
watch it go red.
|
|
697
|
+
|
|
@@ -1,3 +1,26 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: regcode
|
|
3
|
+
Version: 0.12.1
|
|
4
|
+
Summary: Minimalistic coding agent with Python API and CLI
|
|
5
|
+
Requires-Python: >=3.10
|
|
6
|
+
Requires-Dist: boto3>=1.43.40
|
|
7
|
+
Requires-Dist: click>=8.0
|
|
8
|
+
Requires-Dist: crawl4ai>=0.4.0
|
|
9
|
+
Requires-Dist: litellm>=1.83.17
|
|
10
|
+
Requires-Dist: mcp>=1.0.0
|
|
11
|
+
Requires-Dist: mlflow>=3.14.0
|
|
12
|
+
Requires-Dist: openai-agents>=0.18.0
|
|
13
|
+
Requires-Dist: pathspec>=1.1.1
|
|
14
|
+
Requires-Dist: pydantic-monty>=0.0.18
|
|
15
|
+
Requires-Dist: pydantic-settings>=2.14.2
|
|
16
|
+
Requires-Dist: pyperclip>=1.9.0
|
|
17
|
+
Requires-Dist: pytest>=8.0
|
|
18
|
+
Requires-Dist: pyyaml>=6.0
|
|
19
|
+
Requires-Dist: ruff>=0.8
|
|
20
|
+
Requires-Dist: textual>=2.0.0
|
|
21
|
+
Provides-Extra: dev
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
|
|
1
24
|
# RegCode
|
|
2
25
|
|
|
3
26
|
A minimalistic coding agent with Python API bindings and CLI interface.
|
|
@@ -101,6 +124,24 @@ RegCode includes several built-in tools the agent can use:
|
|
|
101
124
|
|
|
102
125
|
All file-writing tools (`write_file`, `patch_file`) include path traversal protection (`..` is blocked). The `patch_file` tool validates line number bounds and type correctness before performing the replacement.
|
|
103
126
|
|
|
127
|
+
### Working directory and confinement
|
|
128
|
+
|
|
129
|
+
`Agent(directory=...)` (and the CLI's `--directory`, which defaults to the current
|
|
130
|
+
directory) is the root every tool is confined to:
|
|
131
|
+
|
|
132
|
+
- Every path-like tool argument (`path`, `file_path`, `script_path`, `directory`) is
|
|
133
|
+
resolved against it and **rejected** when it resolves outside — absolute paths and
|
|
134
|
+
`..` escapes alike.
|
|
135
|
+
- `shell_command` runs with `cwd` set to it, and its arguments are checked the same way,
|
|
136
|
+
so `cat /etc/passwd` is rejected even though `cat` is allowlisted.
|
|
137
|
+
- `python_code` and `run_script` execute inside the Monty sandbox, which has no host
|
|
138
|
+
filesystem access at all.
|
|
139
|
+
|
|
140
|
+
> **`directory` is a confinement boundary, not a security sandbox.** `shell_command`
|
|
141
|
+
> still runs a real subprocess as the current user, and the toolset is only as safe as
|
|
142
|
+
> its permission list. Services embedding RegCode behind an authenticated endpoint should
|
|
143
|
+
> restrict `permissions` (or unregister tools) rather than relying on `directory` alone.
|
|
144
|
+
|
|
104
145
|
## Project Structure
|
|
105
146
|
|
|
106
147
|
```
|
|
@@ -1,25 +1,3 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: regcode
|
|
3
|
-
Version: 0.11.0
|
|
4
|
-
Summary: Minimalistic coding agent with Python API and CLI
|
|
5
|
-
Requires-Python: >=3.10
|
|
6
|
-
Requires-Dist: boto3>=1.43.40
|
|
7
|
-
Requires-Dist: click>=8.0
|
|
8
|
-
Requires-Dist: crawl4ai>=0.4.0
|
|
9
|
-
Requires-Dist: litellm>=1.83.17
|
|
10
|
-
Requires-Dist: mcp>=1.0.0
|
|
11
|
-
Requires-Dist: mlflow>=3.14.0
|
|
12
|
-
Requires-Dist: pathspec>=1.1.1
|
|
13
|
-
Requires-Dist: pydantic-monty>=0.0.18
|
|
14
|
-
Requires-Dist: pydantic-settings>=2.14.2
|
|
15
|
-
Requires-Dist: pyperclip>=1.9.0
|
|
16
|
-
Requires-Dist: pytest>=8.0
|
|
17
|
-
Requires-Dist: pyyaml>=6.0
|
|
18
|
-
Requires-Dist: ruff>=0.8
|
|
19
|
-
Requires-Dist: textual>=2.0.0
|
|
20
|
-
Provides-Extra: dev
|
|
21
|
-
Description-Content-Type: text/markdown
|
|
22
|
-
|
|
23
1
|
# RegCode
|
|
24
2
|
|
|
25
3
|
A minimalistic coding agent with Python API bindings and CLI interface.
|
|
@@ -123,6 +101,24 @@ RegCode includes several built-in tools the agent can use:
|
|
|
123
101
|
|
|
124
102
|
All file-writing tools (`write_file`, `patch_file`) include path traversal protection (`..` is blocked). The `patch_file` tool validates line number bounds and type correctness before performing the replacement.
|
|
125
103
|
|
|
104
|
+
### Working directory and confinement
|
|
105
|
+
|
|
106
|
+
`Agent(directory=...)` (and the CLI's `--directory`, which defaults to the current
|
|
107
|
+
directory) is the root every tool is confined to:
|
|
108
|
+
|
|
109
|
+
- Every path-like tool argument (`path`, `file_path`, `script_path`, `directory`) is
|
|
110
|
+
resolved against it and **rejected** when it resolves outside — absolute paths and
|
|
111
|
+
`..` escapes alike.
|
|
112
|
+
- `shell_command` runs with `cwd` set to it, and its arguments are checked the same way,
|
|
113
|
+
so `cat /etc/passwd` is rejected even though `cat` is allowlisted.
|
|
114
|
+
- `python_code` and `run_script` execute inside the Monty sandbox, which has no host
|
|
115
|
+
filesystem access at all.
|
|
116
|
+
|
|
117
|
+
> **`directory` is a confinement boundary, not a security sandbox.** `shell_command`
|
|
118
|
+
> still runs a real subprocess as the current user, and the toolset is only as safe as
|
|
119
|
+
> its permission list. Services embedding RegCode behind an authenticated endpoint should
|
|
120
|
+
> restrict `permissions` (or unregister tools) rather than relying on `directory` alone.
|
|
121
|
+
|
|
126
122
|
## Project Structure
|
|
127
123
|
|
|
128
124
|
```
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "regcode"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.12.1"
|
|
4
4
|
description = "Minimalistic coding agent with Python API and CLI"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.10"
|
|
7
7
|
dependencies = [
|
|
8
8
|
"crawl4ai>=0.4.0",
|
|
9
9
|
"litellm>=1.83.17",
|
|
10
|
+
"openai-agents>=0.18.0",
|
|
10
11
|
"pydantic-settings>=2.14.2",
|
|
11
12
|
"pyyaml>=6.0",
|
|
12
13
|
"click>=8.0",
|