Release Notes¶
v0.6.2¶
v0.6.1¶
- π¦ PyPI - Build 0.6.1
- πͺ² Bug: Preserve real image MIME type instead of hardcoding image/png (#12)
v0.6.0¶
- π¦ PyPI - Build 0.6.0
- βοΈ Feature: Per-role model fallback chains for Agent (#1)
- πͺ² Bug: Parse fenced tool calls a model narrates as prose (#2)
- πͺ² Bug: Add repetition guard to streaming responses (#3)
- βοΈ Feature: Add per-run JSONL execution trace (#4)
- βοΈ Feature: Add workspace jail toggle and clarify bash's exemption from it (#9)
v0.5.0¶
- βοΈ
pycodeloop --version/-Vprints the installed version and exits β nothing previously let a caller check it without importing the package; the VS Code extension uses this to detect an outdated CLI and offer to update it - βοΈ VS Code extension: CLI update check β on connect, compares the installed
pycodeloop --versionagainst the latest release on PyPI and, if outdated, shows a card with an "Update CodeLoop CLI" button (pip install --user --upgrade pycodeloop)
Security hardening β tools
- β οΈ
sql_query/sql_schemaare nowdangerous(require confirm) and open the connection read-only at the driver level (PRAGMA query_only/SET TRANSACTION READ ONLY) instead of trusting a regex alone. The regex itself got stricter too: a_FORBIDDENlist blocksINTO OUTFILE/INTO DUMPFILE/LOAD_FILE()/pg_read_file()/COPY ... TO/ATTACH DATABASE, andPRAGMAis now checked against an explicit safe-list instead of being allowed wholesale. Connection URLs are validated against an allowlist of SQLAlchemy schemes (sqlite/postgresql/mysql/mariadb variants) before a connection is even attempted - β οΈ
git_commitnow requires an explicitpathslist and refuses-A/-u/--allβ it could previously default togit add -A, contradicting this repo's own "never git add -A" rule - β οΈ Filesystem tools (
read_file,write_file,edit_file,delete_file,grep,glob) are jailed to the workspace root (pycodeloop/tools/_workspace.py) β an absolute path or a..escape outside the process cwd is now refused instead of silently followed - β οΈ
http_request/web_fetch/safe_requestreject any URL whose scheme isn'thttp/httpsbefore the SSRF host check runs, andis_blocked_hostnow also blocks multicast and unspecified addresses - β οΈ
env's sensitive-name matching widened (AUTH,BEARER,COOKIE,SESSION,PASSWD,PASSPHRASE,APIKEY, β¦) and now matches whole name segments instead of a raw substring, so it also masks more accurately
Reliability
- πͺ²
Agentrefuses to run adangeroustool when noconfirmcallback is set instead of running it unconfirmed β closes a fail-open gap whenAgentis used as a library without wiring a confirm handler - πͺ²
pycodeloop servenow emits achat/turnEndnotification after each provider turn, so an editor client can end the current text bubble at the right point instead of concatenating text from before and after a tool call into one run-on message - πͺ²
FileAccessLog/SqliteSessionsdispose their SQLite engine (NullPool+ explicitclose()/__del__) instead of leaking connections/ResourceWarnings across the process lifetime - πͺ² The interactive input-reader thread no longer raises when stdin closes out from under it mid-read
CI / quality gates
- β¬οΈ
mypynow runs with the correct config path in CI;.pre-commit-config.yaml's file glob pointed at the pre-renamecodeloop/package instead ofpycodeloop/; CI now diffstemplates/againstpycodeloop/providers/templates/to catch the two going out of sync - π
rufftarget-version bumped topy310(matchesrequires-python) and the full tree reformatted to it β fullruff check/ruff format --checkpass
v0.4.0¶
pycodeloop core
- βοΈ Sub-agent delegation (
--delegate, off by default) β adelegatetool spawns a fresh sub-agent (same provider, read-only tools:read_file/list_dir/glob/grep/git status/diff/log/web_fetch/sql_schema/sql_query, no write/edit/delete/bash) for an independent subtask. Severaldelegatecalls in the same turn run in parallel - πͺ²
Agent._can_parallelizepreviously forced any repeated tool name in a batch to run sequentially, even for stateless tools β addedTool.concurrent_safe(opt-in, defaultFalse, preserves existing behavior for every built-in tool) sodelegatecan declare its repeated calls safe to run concurrently - βοΈ Persistent project memory (
--memory, on by default) β.pycodeloop/memory.mdauto-loaded into the system prompt every run, plus aremembertool the agent calls when the user corrects its approach or states a standing rule.rememberis gated behind confirmation like every other write tool - πͺ²
write_file/edit_filereject content that looks like a pasted unified diff (@@ ... @@hunk header, or---/+++file headers) instead of writing the diff syntax itself into the file - βοΈ Six more ready-made provider templates: AWS Bedrock (
aws.json, via the bearer-tokenbedrock-mantleendpoint β no SigV4 needed), Kimi/Moonshot AI (kimi.json), DeepSeek (deepseek.json), Llama via Together AI (llama.jsonβ Meta retired its own Llama API on 2026-07-06), Qwen/Alibaba DashScope (qwen.json), NVIDIA NIM (nvidia.json) - πͺ² Grok template was pointing at
grok-4/grok-4-fast/grok-code-fast-1β all retired 2026-05-15. Moved togrok-4.5/grok-4.3/grok-build-0.1. OpenAI template'sgpt-5/gpt-5-mini/gpt-5-nanomoved to the currentgpt-5.6family - πͺ² Gemini 3's thinking models attach
extra_content.google.thought_signatureto tool_calls and reject the next turn if it isn't echoed back unchanged β and thinking can't be disabled on Gemini 3 (minimum isLOW, which still requires the signature). Rather than staying pinned to Gemini 2.5 (itself now 404ing for new API keys),GenericProviderround-trips arbitrary vendor-specific tool_call fields via a newToolCall.extra, so Gemini's default isgemini-3.6-flashagain - βοΈ Anthropic prompt caching β set
"prompt_cache": truein a provider'srequestconfig (already on by default inanthropic.json) to mark the system prompt and the last tool definition withcache_control: {"type": "ephemeral"}, so Anthropic reuses the cached system prompt + tool schema across turns instead of reprocessing them every request (cache reads cost ~10% of a normal input token) - βοΈ Two new tools:
sql_schema(list a database's tables, or one table's columns) andsql_query(a single read-only SELECT/WITH/EXPLAIN/PRAGMA/SHOW/DESCRIBE statement, no writes/DDL/stacked statements) β any SQLAlchemy-supported database via a connection URL - βοΈ Token-saving read cache β
read_filenow logs every read/write/edit/delete to afile_accesstable (~/.pycodeloop/pycodeloop.db), scoped to the session. Reading the exact same path/offset/limit twice with no change on disk in between returns a short "unchanged since you last read" notice instead of repeating the content β passforce=trueto see it again - ποΈ Removed the
todotool (scratchpad checklist) β never adopted, dead weight in the default tool list - πͺ²
http_request/web_fetchhad a DNS-rebinding gap in their SSRF guard: the hostname was resolved once to check it wasn't private/internal, then resolved again, independently, for the actual connection β a rebinding DNS answer between those two lookups could hand the request to a blocked address the check had just approved. Both now resolve once and connect directly to that pinned address (Hostheader + TLS SNI still carry the real hostname, so routing/cert validation are unaffected) - πͺ²
git_diff/git_log/etc never capped their output, unlike every other tool (bash, filesystem,grep,http_request,web_fetch) β a large diff or a deep log could blow out the context.http_request/web_fetchalso each hand-rolled the same 20000-char truncation_limits.truncate()already provides; both now use it - πͺ²
envnow also masks values shaped likescheme://user:pass@host(e.g. aDATABASE_URL), not just names containing SECRET/KEY/TOKEN/PASSWORD/CREDENTIAL;grepskips binary files instead of searching through decoded garbage - π¨
core/split:store/(SQLite/JSON/file sessions, usage tracking, user settings, ORM models),tools/(built-inToolimplementations β theToolABC already lived inabc/, notcore/), and the optional-feature modulesskills.py/memory.py/mcp.pymoved out to be siblings ofcore/, which now only holds the agent engine itself (agent,config,session,codeloop,context_window,exception).clipboard.pymoved intocli/β it's only used by the interactive Textual chat - π¨ New
protocol/module:Messagemoved out ofcore/session.pyintoprotocol/messages.py; the JSON-RPC envelope builders (notification/response/error_response, error codes)pycodeloop servewas hand-assembling inline moved intoprotocol/events.py - π¨
providers/generic.py(one class doing config loading, three response-parsing strategies, a request-builder factory, and raw HTTP/SSE transport) split β the response parsers moved to a newproviders/_responses.py, the config-driven request builder moved intoproviders/_shapes.pynext to the message/tool-schema builders it already used.GenericProvidernow only orchestrates config loading and HTTP transport - π¨
Config._append_to_system_prompthelper extracted (was duplicated between skills discovery and memory loading);tools/__init__.py'sDEFAULT_TOOLS/READ_ONLY_TOOLSno longer double-instantiate the tools they share - ποΈ Removed the
anthropic/openaipoetry extras β dead weight,GenericProvideris stdlib-only and never imports either SDK. Themcpextra (actually used, bypycodeloop/mcp.py) stays
VS Code extension β now its own repo, dotflow-io/vscodeloop
The extension moved out of this repo's vscode-extension/ into its own, with full commit history preserved. It still ships as the pycodeloop/"CodeLoop" VS Code extension; only where the code lives changed.
- βοΈ Provider gallery (β β Select Providerβ¦, or
/provider): card picker for all 13 providers (Anthropic, OpenAI, Gemini, Grok, Groq, AWS Bedrock, Kimi, DeepSeek, Llama, Qwen, NVIDIA NIM, Ollama, LM Studio) with a connected/local/needs-key status per card, a per-provider model picker, and a custom-JSON/generic-URL fallback. API keys and the chosen model are remembered per provider, so switching back doesn't re-prompt - βοΈ Dedicated Sessions page (Sessions toolbar button, replacing the native quickpick): cards show message count, working directory, last-updated time, and an Active badge, each with a Switch action
- βοΈ
pycodeloop.delegationandpycodeloop.memorysettings, gear-menu toggles,/delegateand/memoryslash commands - βοΈ Claude Code-style status line ("β Thinkingβ¦ Β· 12s") replacing the earlier 3-dot bubble β live elapsed-time counter, and switches to "N sub-agents workingβ¦" while parallel
delegatecalls are in flight - βοΈ Completed write_file/edit_file/delete_file tool cards now render the diff computed for the confirmation prompt (colored +/- lines, "Added N lines" summary) instead of discarding it for a bare "Edited path" string
- πͺ² A per-provider model choice that's since been retired by the vendor (e.g. Gemini's
gemini-2.5-flash, no longer available to new API keys) used to keep 404ing every session until manually changed β the extension now self-heals it back to the provider's current default the next time it connects - π¨ Panel reworked to an owline-style visual language: thin 1px borders, sharp corners, monospace uppercase for buttons/labels, SVG line icons instead of emoji, no VS Code default rounded-button chrome β all still on
var(--vscode-*)tokens so it follows the user's editor theme - π¨ "API Key" removed from the gear menu β redundant with the provider gallery's own connect/key flow, which is where the key actually gets set
- π¨
src/restructured from a flat bag oflib/config/webviewfiles intofeatures/(chat, sessions, settings β each with its own controller),vscode/(webview shell, sidebar registration),core-client/(RPC client, process management, wire protocol), andservices/(credentials, settings, storage, terminal, workspace) - π¨
media/main.js(1511 lines, one file) split into 9 plain<script>files (dom,render-utils,chat-turns,chat-tools,chat-apikey,composer,menu,gallery,app) loaded in a fixed order β still no bundler, they share one global scope by design, same as before - π ESLint added (flat config,
typescript-eslint) with annpm run lintscript - π
WebviewMessagediscriminated union replacesonWebviewMessage(message: any); 11 near-identical gear-menu handlers deduplicated into oneonMenuClick(button, action)helper - π
AGENTS.mdadded at the (former) repo root capturing standing rules (provider JSONs synced across template dirs, verify model IDs against current vendor docs, no emoji icons, rebuild+reinstall the.vsixafter every extension change) for skills discovery to pick up automatically
v0.3.0¶
- βοΈ Ready-made provider templates for Gemini (
templates/gemini.json), Grok/xAI (templates/grok.json), and Groq (templates/groq.json) β sameGenericProviderJSON shape, OpenAI-compatible endpoints, no code required - βοΈ VS Code extension (0.3.0): a provider gallery (β β Select Providerβ¦, or
/provider) replaces the flat quickpick β card picker for Anthropic/OpenAI/Gemini/Grok/Groq/Ollama/LM Studio with a connected/local/needs-key status per card, plus a custom-JSON/generic-URL fallback. API keys are now remembered per provider, so switching back doesn't re-prompt. The panel's visual style was also reworked (thin borders, sharp corners, monospace labels) while staying on VS Code's own theme tokens
v0.2.1¶
- βοΈ VS Code extension source restructured by responsibility β thin
extension.tsentrypoint,chatViewProvider.tsfor orchestration,config/settings.tsfor typed config access, purelib/helpers, andwebview/html.tsfor the panel template β plus 26 unit tests (npm test, no new dependency) - βοΈ Skills-discovery toggle and MCP server management (add/remove) added to the extension's gear menu and settings, wired into
pycodeloop serve's existing--no-skills/--mcpflags - βοΈ Slash commands (
/new,/sessions,/provider,/model,/auto-approve,/skills,/mcp,/reload,/settings,/help) with an autocomplete dropdown in the extension's prompt box - πͺ²
Session.history()self-heals a session left with a danglingtool_use(no matchingtool_result) after the process was killed mid-turn β previously left that conversation permanently rejected by the provider - πͺ² Extension's CLI-missing detection fixed (was probing
--version, a flagpycodeloopdoesn't have) and its auto-install now always installs the CLI globally, matchingpycodeloop.command's default of a barepycodeloopresolved offPATH
v0.2.0¶
- βοΈ VS Code extension β a sidebar chat panel talking to
pycodeloop serveover JSON-RPC, with session switching, screenshot/image attachments, auto-approve setting, and Esc-to-cancel - βοΈ
pycodeloop serveβ a JSON-RPC-over-stdio server exposingchat/send,chat/cancel,chat/confirmResponse, andsession/list/session/load, plus a--yesauto-approve flag, for editor integrations - βοΈ
GenericProvideris now the sole provider implementation β dedicated Anthropic/OpenAI SDK-backed providers were removed in favor of the vendor-agnostic JSON-configured HTTP client - βοΈ Agent loop: retries transient provider errors, runs independent tool calls in parallel, auto-compacts older history into the first kept message with tool-result summarization, and a TUI thinking indicator with live context %
- πͺ² Fixed a
CodeLoopsession leak acrosssession_keyswitches and a doubledpypycodelooptypo in install docs - π README and docs updated for the
GenericProvider-only model; repository moved todotflow-io/pycodeloop
v0.1.0¶
Initial release.
- βοΈ Core agent loop (
Agent), dependency-injection container (Config), conversation state (Session), and theCodeLoopentrypoint - βοΈ
ProviderABC withGenericProviderβ any HTTP chat-completions-style API via the stdlib, no vendor SDK, configured declaratively for Anthropic, OpenAI, Ollama, and other backends via JSON (seetemplates/) - βοΈ
ToolABC with built-inread_file,write_file,edit_file,delete_file,list_dir,glob,grep,bash,web_fetch,http_request,git_status,git_diff,git_log,git_commit,env,todo - βοΈ Dangerous-tool confirmation gate with diff/command preview
- βοΈ Streaming text output and per-turn/cumulative token usage tracking
- βοΈ Auto-compaction of older conversation history and retry with backoff on transient provider failures
- βοΈ MCP client β connect to any Model Context Protocol server over stdio and use its tools like local ones
- βοΈ Skills discovery β Claude Code, Cursor, and
AGENTS.mdskills/instructions found on disk are exposed as aread_skilltool - βοΈ Custom and local providers β dotted-path loading (
module.path:ClassName) andbase_urlsupport for any OpenAI-compatible server - βοΈ
pycodeloopCLI (run,chat,serve) with permission prompts, streaming, and token usage reporting βservespeaks JSON-RPC over stdio for editor integrations like the VS Code extension