Process termination guardrail - #3
Open
collin-kierce wants to merge 41 commits into
Open
Conversation
Migrate 49 user-facing emit_* strings to the t() catalog; 49 → 0 raw sites. Key groupings (48 new oauth.* keys): oauth.server.* — callback server startup, ports, redirect-URI, paste hint oauth.pasteback.* — paste-back parse/provider errors, no-code, no-state oauth.state_mismatch / oauth.callback.* — state check, callback error/timeout oauth.browser.* — headless mode, opening, fallback URL, manual URL, open-failed oauth.auth.* — token exchange, save, success, model discovery oauth.reauth.* — refresh-failed, restored, no-token oauth.cmd.auth.* — starting, overwrite-warning oauth.cmd.status.* — authenticated, expires, models, no-models, not-authenticated, hint oauth.cmd.fast.* — wrong-model, enabled, enabled-detail, disabled oauth.cmd.logout.* — tokens-removed, models-removed, success oauth.model.* — no-api-key Tests: tests/i18n/test_claude_oauth_i18n.py (14 tests). All i18n tests pass. ruff clean.
Two bugs fixed:
1. f-string false positives (_has_string_literal)
Previously any ast.JoinedStr was unconditionally classified as 'raw',
meaning pure-variable f-strings like f"{result}" or f" {msg}" were
counted as un-extracted literals even though they contain no translatable
content. Fixed by walking the f-string's values list and only returning
True when at least one ast.Constant part has non-whitespace text.
Impact: 23 false positives reclassified as 'dynamic' (1302 -> 1279 raw).
2. Single-file path silently returning 0 sites (_iter_py_files)
os.walk(file_path) on a regular file yields nothing, so
'python -m code_puppy.i18n.audit path/to/module.py' always reported
0 sites. Fixed by detecting a file path up front and yielding it
directly if it ends in .py.
Tests added (5 new, 25 total):
test_fstring_pure_variable_is_dynamic
test_fstring_whitespace_only_literal_is_dynamic
test_fstring_with_content_and_variable_is_raw
test_fstring_with_only_arrow_is_raw
test_single_file_path_is_accepted
test_single_file_pure_variable_fstring_not_raw
All 25 audit tests pass. ruff clean.
…ude_code_oauth)
Adds a 'dangerous_command_guard_allow' config key: a comma-separated list of pattern names (e.g. 'git reset --hard, --force') that bypass the destructive command guard AND the force push guard, while every other dangerous pattern stays protected. Previously the only escape was 'disable_dangerous_command_guard', which is all-or-nothing and kills both guards at once. The allowlist is shared across both guards (keyed off the detector pattern_name, normalized for case/whitespace) and is fully additive: the legacy disable flag is untouched and still wins as a global kill-switch. config.py: normalize_guard_pattern_name(), get_dangerous_command_guard_allowlist(), is_dangerous_command_allowlisted(); register key for /set autocomplete. Both guard callbacks wave through allowlisted patterns before prompting/blocking. Adds tests/plugins/test_command_guard_allowlist.py (14 tests); updates test_config key-set assertions.
Nested AGENTS.md so any agent (including Code Puppy itself) editing the destructive command guard auto-loads the rules: file map, callback precedence, the add-a-pattern recipe, the pattern_name-is-an-API-contract rule, false-positive discipline, and the shared allowlist. Scoped to the destructive command guard only (force-push guard noted as a sibling).
…OME)
Run `code-puppy` from your home directory and every launch warns about a
file it already loaded:
Project MCP config '/home/you/.code_puppy/mcp_servers.json' is not
trusted yet; its servers are NOT loaded. Review it, then run
'/mcp trust' to accept
When CWD is $HOME, <CWD>/.code_puppy/mcp_servers.json and
~/.code_puppy/mcp_servers.json are the SAME PATH. get_project_mcp_servers_file()
returned that path as a project candidate, so the trust gate flagged the
user-level config as an untrusted stranger -- while load_mcp_server_configs()
had already merged its servers in via the user-level source. The warning is
false: the servers are loaded, and it says they are not.
That is worse than noise. It tells users their MCP tools are unavailable
when they are working fine, and the remedy it names is ceremony over a
decision the user already made by writing the file. It also fires on every
launch, since nothing was ever written to trusted_mcp.json.
Fix: get_project_mcp_servers_file() returns None when the candidate IS the
user-level config. There is no second, less-trusted source to gate, so
there is nothing to ask about. Compared with samefile() rather than path
equality so a symlink or bind-mount pointing at the user-level config is
recognized too.
One chokepoint by design: get_trust_status(), load_project_mcp_server_configs()
and `/mcp trust` all already handle "no project file" correctly, so a single
guard fixes the whole path without growing a second code path.
The real gate is untouched. A genuinely separate project config is still
withheld until trusted, asserted in both directions:
assert cp_config.load_mcp_server_configs() == {"user_only": "u"} # gated
assert pc.trust_project_mcp() is True
assert "proj_only" in cp_config.load_mcp_server_configs() # honored
The check targets config.MCP_SERVERS_FILE, never $HOME, so it stays correct
when XDG_CONFIG_HOME relocates the user-level config out of ~/.code_puppy.
4 tests added; 3 fail without the change (14 -> 17 in that file).
- Rename `_supports_ultra_reasoning` to `_supports_max_reasoning` and update its docstring - Replace `supports_ultra_reasoning` config key with `supports_max_reasoning` in model metadata - Update `SETTING_DEFINITIONS` reasoning_effort choices from `["minimal", ..., "ultra"]` to `["none", ..., "max"]` - Update capability-gating logic in `_get_setting_choices` to filter `max` instead of `ultra` - Update docstring to reflect the renamed `max` effort level
… effort values - Normalize legacy effort values at request time: `minimal` → `none`, `ultra` → `max` - Ensures existing saved configs with old effort labels continue to work transparently - Aliases are applied only for GPT-5 models before sending `openai_reasoning_effort`
…nfig keys - Replace references to `ultra` with `max` and `minimal` with `none` in all test assertions - Rename test methods `test_reasoning_effort_with_xhigh_but_without_ultra` → `…without_max` and `test_reasoning_effort_with_ultra` → `…with_max` - Update model config fixture keys from `supports_ultra_reasoning` to `supports_max_reasoning` - Remove redundant `supports_xhigh_reasoning` assertion for `gpt-5.5` (already covered elsewhere)
Adds a 'Custom Params' entry to the /model_settings TUI where free-form
key = value pairs can be added, edited, and deleted per model. Pairs are
stored as a JSON blob under the reserved model_settings_<model>_custom
config key, and merged into extra_body by make_model_settings.
- Dotted keys expand into nested dicts:
chat_template_kwargs.thinking = medium ->
{"chat_template_kwargs": {"thinking": "medium"}}
- Values parse as bool/int/float, falling back to string
- Applied last, after every model-specific branch, so custom values
override built-in settings on conflict
- Corrupt/non-dict JSON fails closed to an empty mapping
- Reserved 'custom' key is excluded from the generic scalar settings
namespace so it can never leak raw JSON into ModelSettings
…t/replacements/snippet) (mpfaffenberger#694)
/fork called _invoke_agent_impl with no session_id, so every fork started with a blank context instead of branching the conversation (unlike ACP's fork_session, which copies history properly). - _seed_fork_session snapshots the current agent's live message history synchronously in _start_fork (point-in-time, safe mid-run: the pause gate parks the agent and the history processor only commits at model-request boundaries ending in a ModelRequest) and persists it via the existing sub-agent session machinery - the seeded session_id flows into _invoke_agent_impl, which loads it as a continuing session; empty history or snapshot failure falls back to a fresh context with a warning - tests: snapshot copy semantics, session id threading, fresh-context fallback, failure fallback; fix fake impl session_id kwarg mismatch Also repair stale/leaky tests found running the full suite: - model_settings_menu: Custom Params is now universally offered (d08b6c9), so 'no configurable settings' premises were unreachable; assert new behavior + keep defensive empty-state branches covered - computer_use inline_image: scrub terminal-identity env vars so running the suite inside iTerm2/kitty can't leak into detection
…ot-be-its-own-project-twin fix(mcp): a config can't be its own untrusted project twin (CWD == $HOME)
…-claude-oauth feat(i18n): extract claude_code_oauth/register_callbacks.py strings
…command-guard-allowlist feat(guards): granular per-pattern allowlist for command guards
…lse-positives fix(i18n/audit): eliminate false positives in raw-site classification
…ted new self_termination_guardrail
…tion_guardrail does not allow for disabling
… token in the command is the executable then check only that token
collin-kierce
force-pushed
the
process_termination_guardrail
branch
from
August 3, 2026 19:28
68e1068 to
25f9985
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.