cli/config: --auto-continue flag and [agent] auto_continue_limits

This commit is contained in:
2026-09-09 22:47:39 +08:00
parent aba3765def
commit 18cbcf6835
7 changed files with 73 additions and 7 deletions
+20 -4
View File
@@ -124,9 +124,14 @@ def _read_prompt_text(prompt: str | None, *, stdin_isatty: bool) -> str | None:
return text or None
def _setup_hooks(*, interactive: bool) -> None:
"""Install interactive limit hooks (workspace policy is set on ReplState)."""
if interactive:
def _setup_hooks(*, interactive: bool, auto_continue: bool) -> None:
"""Install limit hooks (workspace policy is set on ReplState).
``auto_continue`` keeps the PTY/limit hook installed for one-shot runs so
``--auto-continue`` / ``[agent] auto_continue_limits`` can raise limits
without a prompt; otherwise non-interactive runs keep today's deny.
"""
if interactive or auto_continue:
install_cli_limit_hooks()
else:
from plyngent.tools.process.pty_session import PtyManager
@@ -221,6 +226,7 @@ async def _run_chat( # noqa: C901, PLR0912, PLR0915 — chat orchestration
prompt_text: str | None,
stream: bool,
yes: bool,
auto_continue: bool,
quiet: bool,
) -> int:
oneshot = prompt_text is not None
@@ -236,7 +242,8 @@ async def _run_chat( # noqa: C901, PLR0912, PLR0915 — chat orchestration
if store.recoverable_providers:
_warn_recoverable_providers(store.recoverable_providers)
_setup_hooks(interactive=interactive)
auto_effective = auto_continue or store.agent_config.auto_continue_limits
_setup_hooks(interactive=interactive, auto_continue=auto_effective)
# --yes forces sticky YOLO; else derive from config.confirm_destructive.
from plyngent.cli.state import YoloMode
@@ -315,6 +322,7 @@ async def _run_chat( # noqa: C901, PLR0912, PLR0915 — chat orchestration
max_rounds=max_rounds,
stream_enabled=stream,
interactive_limits=interactive,
auto_continue_limits=auto_effective,
yolo=yolo,
mcp_manager=mcp_manager,
)
@@ -480,6 +488,12 @@ def main(ctx: click.Context, log_level: str) -> None:
default=False,
help="Enable YOLO: skip destructive-tool confirms (sticky for this process).",
)
@click.option(
"--auto-continue",
is_flag=True,
default=False,
help="Auto-raise tool/PTY limits without prompting (like answering yyy).",
)
@click.option(
"--quiet",
is_flag=True,
@@ -498,6 +512,7 @@ def chat_cmd(
prompt: str | None,
stream: bool, # noqa: FBT001
yes: bool, # noqa: FBT001
auto_continue: bool, # noqa: FBT001
quiet: bool, # noqa: FBT001
) -> None:
"""Interactive chat REPL, or one-shot with ``-p`` / stdin.
@@ -538,6 +553,7 @@ def chat_cmd(
prompt_text=prompt_text,
stream=stream,
yes=yes,
auto_continue=auto_continue,
quiet=quiet,
)
)
+4 -1
View File
@@ -36,14 +36,17 @@ def _echo_interrupted() -> None:
async def run_repl(state: ReplState) -> None:
"""Interactive chat loop with readline editing, history, and Tab completion."""
setup_readline(state)
from plyngent.cli.limits import auto_continue_enabled
yolo = state.effective_yolo()
yolo_part = f" yolo={yolo}" if yolo != "off" else ""
auto_part = " limit_auto=on" if auto_continue_enabled() else ""
click.echo(
f"plyngent chat provider={state.provider_name} model={state.model} "
f"session={state.session_id} tools={'on' if state.tools_enabled else 'off'} "
f"rounds={state.max_rounds} messages={len(state.agent.messages)} "
f"stream={'on' if state.agent.stream else 'off'} "
f"verbose={'on' if state.verbose else 'off'}{yolo_part}"
f"verbose={'on' if state.verbose else 'off'}{yolo_part}{auto_part}"
)
click.echo('Type /help for commands. Multiline: """ … """. Empty line is ignored.')
+7 -2
View File
@@ -69,6 +69,8 @@ class ReplState:
markdown_enabled: bool = True
# One-shot / scripts: never prompt to raise tool-loop limits.
interactive_limits: bool = True
# Auto-raise tool/PTY limits without prompting (--auto-continue / config).
auto_continue_limits: bool = False
# Soft destructive-tool confirms: None → derive from config.confirm_destructive.
# off = confirm; on = skip (sticky); once = skip next user turn then off.
yolo: YoloMode | None = None
@@ -97,6 +99,9 @@ class ReplState:
self.instance_state.workspace_root = self.workspace
self.instance_state.workspace.root = self.workspace
self._install_config_access()
from plyngent.cli.limits import set_auto_continue_default
set_auto_continue_default(enabled=self.auto_continue_limits)
self.session_state = self._session_data_for_todo()
self.agent = self._make_agent()
self.sync_display_flags()
@@ -340,7 +345,7 @@ class ReplState:
return "\n\n".join(parts)
def _make_agent(self) -> ChatAgent:
from plyngent.cli.limits import prompt_continue_limit_async
from plyngent.cli.limits import auto_continue_enabled, prompt_continue_limit_async
from plyngent.config import compose_agent_system_content
agent_cfg = self.config.agent_config
@@ -350,7 +355,7 @@ class ReplState:
agent_cfg.tool_directives,
mcp_text,
)
on_limit = prompt_continue_limit_async if self.interactive_limits else None
on_limit = prompt_continue_limit_async if (self.interactive_limits or auto_continue_enabled()) else None
peak = 0
band = 0
last_req = None
+3
View File
@@ -117,6 +117,9 @@ class AgentConfig(Struct, omit_defaults=True):
# Static directory pre-allow (resolved path → mode); never prompts.
# Denylist still applies, and the path must exist when chat starts.
allow_paths: dict[str, AllowPathMode] = field(default_factory=dict)
# Auto-raise tool/PTY limits without prompting (same as answering ``yyy``);
# process-wide, so every turn skips the limit confirm.
auto_continue_limits: bool = False
max_context_tokens: int = 200_000
# How to inject todo stack nags into model context (see agent/todo_nag.py).