mindcode 0.5.1__tar.gz → 0.5.3__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.
- {mindcode-0.5.1 → mindcode-0.5.3}/PKG-INFO +1 -1
- {mindcode-0.5.1 → mindcode-0.5.3}/mindcode/_version.py +1 -1
- {mindcode-0.5.1 → mindcode-0.5.3}/mindcode/acp_agent/agent.py +65 -6
- {mindcode-0.5.1 → mindcode-0.5.3}/mindcode/bench/schema.py +5 -3
- {mindcode-0.5.1 → mindcode-0.5.3}/mindcode/cli/__init__.py +10 -3
- {mindcode-0.5.1 → mindcode-0.5.3}/mindcode/cli/commands/bench.py +15 -3
- {mindcode-0.5.1 → mindcode-0.5.3}/mindcode/cli/commands/chat.py +36 -6
- {mindcode-0.5.1 → mindcode-0.5.3}/mindcode/cli/commands/shell.py +38 -6
- {mindcode-0.5.1 → mindcode-0.5.3}/mindcode/cli/commands/task.py +26 -8
- {mindcode-0.5.1 → mindcode-0.5.3}/mindcode/cli/shell/input_pipeline.py +13 -0
- {mindcode-0.5.1 → mindcode-0.5.3}/mindcode/cli/shell/repl.py +124 -40
- {mindcode-0.5.1 → mindcode-0.5.3}/mindcode/cli/shell/slash.py +6 -24
- {mindcode-0.5.1 → mindcode-0.5.3}/mindcode/cli/shell/tui.py +128 -76
- mindcode-0.5.3/mindcode/config/settings.toml +41 -0
- {mindcode-0.5.1 → mindcode-0.5.3}/mindcode/config.py +173 -2
- {mindcode-0.5.1 → mindcode-0.5.3}/mindcode/render.py +13 -4
- {mindcode-0.5.1 → mindcode-0.5.3}/mindcode/runtime.py +43 -16
- {mindcode-0.5.1 → mindcode-0.5.3}/mindcode/tasking.py +8 -5
- {mindcode-0.5.1 → mindcode-0.5.3}/mindcode.egg-info/PKG-INFO +1 -1
- {mindcode-0.5.1 → mindcode-0.5.3}/mindcode.egg-info/SOURCES.txt +1 -0
- {mindcode-0.5.1 → mindcode-0.5.3}/pyproject.toml +4 -1
- {mindcode-0.5.1 → mindcode-0.5.3}/tests/test_acp_agent.py +56 -3
- {mindcode-0.5.1 → mindcode-0.5.3}/tests/test_bench.py +0 -12
- {mindcode-0.5.1 → mindcode-0.5.3}/tests/test_cli_commands.py +95 -0
- {mindcode-0.5.1 → mindcode-0.5.3}/tests/test_config.py +43 -1
- mindcode-0.5.3/tests/test_provider_retry_defaults.py +185 -0
- {mindcode-0.5.1 → mindcode-0.5.3}/tests/test_render_exec.py +62 -0
- {mindcode-0.5.1 → mindcode-0.5.3}/tests/test_shell_channels.py +61 -55
- {mindcode-0.5.1 → mindcode-0.5.3}/tests/test_shell_interrupt.py +72 -19
- {mindcode-0.5.1 → mindcode-0.5.3}/tests/test_tasking.py +2 -0
- mindcode-0.5.1/tests/test_provider_retry_defaults.py +0 -80
- {mindcode-0.5.1 → mindcode-0.5.3}/README.md +0 -0
- {mindcode-0.5.1 → mindcode-0.5.3}/mindcode/__init__.py +0 -0
- {mindcode-0.5.1 → mindcode-0.5.3}/mindcode/__main__.py +0 -0
- {mindcode-0.5.1 → mindcode-0.5.3}/mindcode/acp_agent/__init__.py +0 -0
- {mindcode-0.5.1 → mindcode-0.5.3}/mindcode/acp_agent/__main__.py +0 -0
- {mindcode-0.5.1 → mindcode-0.5.3}/mindcode/approval.py +0 -0
- {mindcode-0.5.1 → mindcode-0.5.3}/mindcode/bench/__init__.py +0 -0
- {mindcode-0.5.1 → mindcode-0.5.3}/mindcode/bench/adapters/__init__.py +0 -0
- {mindcode-0.5.1 → mindcode-0.5.3}/mindcode/bench/adapters/base.py +0 -0
- {mindcode-0.5.1 → mindcode-0.5.3}/mindcode/bench/adapters/mini_bfcl.py +0 -0
- {mindcode-0.5.1 → mindcode-0.5.3}/mindcode/bench/adapters/mini_gaia.py +0 -0
- {mindcode-0.5.1 → mindcode-0.5.3}/mindcode/bench/adapters/mini_terminal.py +0 -0
- {mindcode-0.5.1 → mindcode-0.5.3}/mindcode/bench/adapters/swe_bench.py +0 -0
- {mindcode-0.5.1 → mindcode-0.5.3}/mindcode/bench/adapters/terminal_bench.py +0 -0
- {mindcode-0.5.1 → mindcode-0.5.3}/mindcode/bench/fake_tools.py +0 -0
- {mindcode-0.5.1 → mindcode-0.5.3}/mindcode/bench/jsonutil.py +0 -0
- {mindcode-0.5.1 → mindcode-0.5.3}/mindcode/bench/paths.py +0 -0
- {mindcode-0.5.1 → mindcode-0.5.3}/mindcode/bench/predictions.py +0 -0
- {mindcode-0.5.1 → mindcode-0.5.3}/mindcode/bench/report.py +0 -0
- {mindcode-0.5.1 → mindcode-0.5.3}/mindcode/bench/runner.py +0 -0
- {mindcode-0.5.1 → mindcode-0.5.3}/mindcode/bench/scorers/base.py +0 -0
- {mindcode-0.5.1 → mindcode-0.5.3}/mindcode/bench/scorers/composite.py +0 -0
- {mindcode-0.5.1 → mindcode-0.5.3}/mindcode/bench/scorers/exact.py +0 -0
- {mindcode-0.5.1 → mindcode-0.5.3}/mindcode/bench/scorers/json_call.py +0 -0
- {mindcode-0.5.1 → mindcode-0.5.3}/mindcode/bench/scorers/swe.py +0 -0
- {mindcode-0.5.1 → mindcode-0.5.3}/mindcode/bench/scorers/terminal.py +0 -0
- {mindcode-0.5.1 → mindcode-0.5.3}/mindcode/bench/scorers/terminal_bench.py +0 -0
- {mindcode-0.5.1 → mindcode-0.5.3}/mindcode/bench/trace.py +0 -0
- {mindcode-0.5.1 → mindcode-0.5.3}/mindcode/bench/workspace.py +0 -0
- {mindcode-0.5.1 → mindcode-0.5.3}/mindcode/cli/_shared.py +0 -0
- {mindcode-0.5.1 → mindcode-0.5.3}/mindcode/cli/commands/__init__.py +0 -0
- {mindcode-0.5.1 → mindcode-0.5.3}/mindcode/cli/commands/config_cmd.py +0 -0
- {mindcode-0.5.1 → mindcode-0.5.3}/mindcode/cli/commands/remote.py +0 -0
- {mindcode-0.5.1 → mindcode-0.5.3}/mindcode/cli/commands/status.py +0 -0
- {mindcode-0.5.1 → mindcode-0.5.3}/mindcode/cli/commands/terminal.py +0 -0
- {mindcode-0.5.1 → mindcode-0.5.3}/mindcode/cli/shell/__init__.py +0 -0
- {mindcode-0.5.1 → mindcode-0.5.3}/mindcode/cli/shell/completion.py +0 -0
- {mindcode-0.5.1 → mindcode-0.5.3}/mindcode/cli/shell/menu.py +0 -0
- {mindcode-0.5.1 → mindcode-0.5.3}/mindcode/cli/shell/startup.py +0 -0
- {mindcode-0.5.1 → mindcode-0.5.3}/mindcode/mcp.py +0 -0
- {mindcode-0.5.1 → mindcode-0.5.3}/mindcode/policy.py +0 -0
- {mindcode-0.5.1 → mindcode-0.5.3}/mindcode/remote.py +0 -0
- {mindcode-0.5.1 → mindcode-0.5.3}/mindcode/skills.py +0 -0
- {mindcode-0.5.1 → mindcode-0.5.3}/mindcode/subagents.py +0 -0
- {mindcode-0.5.1 → mindcode-0.5.3}/mindcode/terminal/__init__.py +0 -0
- {mindcode-0.5.1 → mindcode-0.5.3}/mindcode/terminal/__main__.py +0 -0
- {mindcode-0.5.1 → mindcode-0.5.3}/mindcode/terminal_core.py +0 -0
- {mindcode-0.5.1 → mindcode-0.5.3}/mindcode.egg-info/dependency_links.txt +0 -0
- {mindcode-0.5.1 → mindcode-0.5.3}/mindcode.egg-info/entry_points.txt +0 -0
- {mindcode-0.5.1 → mindcode-0.5.3}/mindcode.egg-info/requires.txt +0 -0
- {mindcode-0.5.1 → mindcode-0.5.3}/mindcode.egg-info/top_level.txt +0 -0
- {mindcode-0.5.1 → mindcode-0.5.3}/setup.cfg +0 -0
- {mindcode-0.5.1 → mindcode-0.5.3}/tests/test_approval.py +0 -0
- {mindcode-0.5.1 → mindcode-0.5.3}/tests/test_diff_capture.py +0 -0
- {mindcode-0.5.1 → mindcode-0.5.3}/tests/test_exec_command_guard.py +0 -0
- {mindcode-0.5.1 → mindcode-0.5.3}/tests/test_external_file_approval.py +0 -0
- {mindcode-0.5.1 → mindcode-0.5.3}/tests/test_policy_choice.py +0 -0
- {mindcode-0.5.1 → mindcode-0.5.3}/tests/test_policy_timeout.py +0 -0
- {mindcode-0.5.1 → mindcode-0.5.3}/tests/test_reasoner_compat.py +0 -0
- {mindcode-0.5.1 → mindcode-0.5.3}/tests/test_remote.py +0 -0
- {mindcode-0.5.1 → mindcode-0.5.3}/tests/test_shell_completion.py +0 -0
- {mindcode-0.5.1 → mindcode-0.5.3}/tests/test_shell_session.py +0 -0
- {mindcode-0.5.1 → mindcode-0.5.3}/tests/test_shell_slash_priority.py +0 -0
- {mindcode-0.5.1 → mindcode-0.5.3}/tests/test_shell_tui.py +0 -0
- {mindcode-0.5.1 → mindcode-0.5.3}/tests/test_skills_mcp.py +0 -0
- {mindcode-0.5.1 → mindcode-0.5.3}/tests/test_slash_resume.py +0 -0
- {mindcode-0.5.1 → mindcode-0.5.3}/tests/test_subagents.py +0 -0
- {mindcode-0.5.1 → mindcode-0.5.3}/tests/test_system_prompt.py +0 -0
- {mindcode-0.5.1 → mindcode-0.5.3}/tests/test_task_resume.py +0 -0
- {mindcode-0.5.1 → mindcode-0.5.3}/tests/test_terminal.py +0 -0
|
@@ -34,7 +34,6 @@ from acp.schema import (
|
|
|
34
34
|
PermissionOption,
|
|
35
35
|
PromptCapabilities,
|
|
36
36
|
PromptResponse,
|
|
37
|
-
RequestPermissionResponse,
|
|
38
37
|
TextContentBlock,
|
|
39
38
|
ToolCallStart,
|
|
40
39
|
)
|
|
@@ -43,6 +42,8 @@ from mindagent.core import (
|
|
|
43
42
|
AgentSession,
|
|
44
43
|
AgentRuntime,
|
|
45
44
|
EventType,
|
|
45
|
+
ExternalFileReadDecision,
|
|
46
|
+
ExternalFileReadRequest,
|
|
46
47
|
LocalConversationStore,
|
|
47
48
|
RunOutcome,
|
|
48
49
|
SessionEnvironment,
|
|
@@ -50,7 +51,7 @@ from mindagent.core import (
|
|
|
50
51
|
SessionPolicy,
|
|
51
52
|
)
|
|
52
53
|
from ..approval import describe_action
|
|
53
|
-
from ..config import ConfigError, ConfigStore, ProviderConfig
|
|
54
|
+
from ..config import BUILTIN_SETTINGS, ConfigError, ConfigStore, ProviderConfig
|
|
54
55
|
from ..policy import ApprovalMode
|
|
55
56
|
from ..runtime import build_runtime
|
|
56
57
|
|
|
@@ -58,9 +59,6 @@ logger = logging.getLogger("mindcode.acp_agent")
|
|
|
58
59
|
|
|
59
60
|
PROTOCOL_VERSION = 1 # stable ACP v1; see docs/acp-layering.md §5
|
|
60
61
|
|
|
61
|
-
_PERMISSION_TIMEOUT_S = 120.0
|
|
62
|
-
|
|
63
|
-
|
|
64
62
|
def _stop_reason(result: Any) -> str:
|
|
65
63
|
"""Map a native run result to an ACP v1 stop reason (layering §3)."""
|
|
66
64
|
outcome = getattr(result, "outcome", None)
|
|
@@ -123,12 +121,22 @@ class _AgentHarness:
|
|
|
123
121
|
config_store: ConfigStore | None = None,
|
|
124
122
|
cwd: Path | None = None,
|
|
125
123
|
subagents: bool = False,
|
|
126
|
-
permission_timeout_s: float =
|
|
124
|
+
permission_timeout_s: float | None = None,
|
|
127
125
|
) -> None:
|
|
128
126
|
self._conn = conn
|
|
129
127
|
self._config_store = config_store or ConfigStore()
|
|
130
128
|
self._cwd = cwd or Path.cwd()
|
|
131
129
|
self._subagents = subagents
|
|
130
|
+
self._permission_timeout_from_config = permission_timeout_s is None
|
|
131
|
+
if permission_timeout_s is None:
|
|
132
|
+
try:
|
|
133
|
+
permission_timeout_s = (
|
|
134
|
+
self._config_store.read()
|
|
135
|
+
.runtime_settings.permission_timeout_s
|
|
136
|
+
)
|
|
137
|
+
except AttributeError:
|
|
138
|
+
# Lightweight test/fake stores may only implement resolve().
|
|
139
|
+
permission_timeout_s = BUILTIN_SETTINGS.permission_timeout_s
|
|
132
140
|
self._permission_timeout_s = permission_timeout_s
|
|
133
141
|
self._sessions: dict[str, _ManagedSession] = {}
|
|
134
142
|
self._new_session_lock = asyncio.Lock()
|
|
@@ -171,6 +179,10 @@ class _AgentHarness:
|
|
|
171
179
|
resolved = self._config_store.resolve()
|
|
172
180
|
except ConfigError as exc:
|
|
173
181
|
raise RequestError.internal_error(str(exc)) from exc
|
|
182
|
+
if self._permission_timeout_from_config:
|
|
183
|
+
self._permission_timeout_s = (
|
|
184
|
+
resolved.runtime_settings.permission_timeout_s
|
|
185
|
+
)
|
|
174
186
|
store = LocalConversationStore(
|
|
175
187
|
self._config_store.root / "sessions"
|
|
176
188
|
)
|
|
@@ -181,6 +193,7 @@ class _AgentHarness:
|
|
|
181
193
|
stream=True,
|
|
182
194
|
event_handler=None,
|
|
183
195
|
approval_callback=self._make_approval(resolved),
|
|
196
|
+
file_access_callback=self._make_file_access_approval(),
|
|
184
197
|
)
|
|
185
198
|
try:
|
|
186
199
|
session = runtime.create_session(
|
|
@@ -309,6 +322,52 @@ class _AgentHarness:
|
|
|
309
322
|
|
|
310
323
|
return approve
|
|
311
324
|
|
|
325
|
+
def _make_file_access_approval(self) -> Any:
|
|
326
|
+
async def approve(
|
|
327
|
+
context: Any,
|
|
328
|
+
request: ExternalFileReadRequest,
|
|
329
|
+
) -> ExternalFileReadDecision:
|
|
330
|
+
managed = self._managed(context.session_id)
|
|
331
|
+
started = helpers.start_tool_call(
|
|
332
|
+
tool_call_id=f"external-read-{request.request_id}",
|
|
333
|
+
title=f"Read external file {request.path}",
|
|
334
|
+
kind="read",
|
|
335
|
+
status="pending",
|
|
336
|
+
)
|
|
337
|
+
await self._notify_update(managed, started)
|
|
338
|
+
options = [
|
|
339
|
+
PermissionOption(
|
|
340
|
+
option_id="allow", name="Allow once", kind="allow_once"
|
|
341
|
+
),
|
|
342
|
+
PermissionOption(
|
|
343
|
+
option_id="reject", name="Reject", kind="reject_once"
|
|
344
|
+
),
|
|
345
|
+
]
|
|
346
|
+
approved = False
|
|
347
|
+
try:
|
|
348
|
+
response = await asyncio.wait_for(
|
|
349
|
+
self._conn.request_permission(
|
|
350
|
+
session_id=managed.session_id,
|
|
351
|
+
tool_call=started,
|
|
352
|
+
options=options,
|
|
353
|
+
),
|
|
354
|
+
timeout=self._permission_timeout_s,
|
|
355
|
+
)
|
|
356
|
+
except asyncio.TimeoutError:
|
|
357
|
+
logger.warning(
|
|
358
|
+
"external read permission timed out after %.0fs → deny",
|
|
359
|
+
self._permission_timeout_s,
|
|
360
|
+
)
|
|
361
|
+
else:
|
|
362
|
+
outcome = response.outcome
|
|
363
|
+
approved = (
|
|
364
|
+
getattr(outcome, "outcome", None) != "cancelled"
|
|
365
|
+
and getattr(outcome, "option_id", None) == "allow"
|
|
366
|
+
)
|
|
367
|
+
return ExternalFileReadDecision(request.request_id, approved)
|
|
368
|
+
|
|
369
|
+
return approve
|
|
370
|
+
|
|
312
371
|
# ----- event-stream translation (native port 5 → session/update) -----
|
|
313
372
|
|
|
314
373
|
async def _notify_update(self, managed: _ManagedSession, update: Any) -> None:
|
|
@@ -4,6 +4,8 @@ from dataclasses import dataclass, field
|
|
|
4
4
|
from pathlib import Path
|
|
5
5
|
from typing import Any
|
|
6
6
|
|
|
7
|
+
from ..config import BUILTIN_SETTINGS
|
|
8
|
+
|
|
7
9
|
|
|
8
10
|
@dataclass(frozen=True)
|
|
9
11
|
class BenchmarkFile:
|
|
@@ -59,9 +61,9 @@ class BenchmarkRunConfig:
|
|
|
59
61
|
limit: int | None = None
|
|
60
62
|
case_ids: tuple[str, ...] = ()
|
|
61
63
|
concurrency: int = 1
|
|
62
|
-
max_steps: int =
|
|
63
|
-
step_timeout_s: float | None =
|
|
64
|
-
total_timeout_s: float | None =
|
|
64
|
+
max_steps: int = BUILTIN_SETTINGS.benchmark.max_steps
|
|
65
|
+
step_timeout_s: float | None = BUILTIN_SETTINGS.benchmark.step_timeout_s
|
|
66
|
+
total_timeout_s: float | None = BUILTIN_SETTINGS.benchmark.total_timeout_s
|
|
65
67
|
enable_subagents: bool = False
|
|
66
68
|
enable_background_tasks: bool = False
|
|
67
69
|
seed: int | None = None
|
|
@@ -20,7 +20,13 @@
|
|
|
20
20
|
from __future__ import annotations
|
|
21
21
|
|
|
22
22
|
import typer
|
|
23
|
-
|
|
23
|
+
|
|
24
|
+
try:
|
|
25
|
+
# Typer 0.27 vendors Click to keep its exception types aligned with its
|
|
26
|
+
# renderer; older Typer releases use the public Click package directly.
|
|
27
|
+
from typer.core import _click
|
|
28
|
+
except ImportError: # pragma: no cover - compatibility with older Typer
|
|
29
|
+
import click as _click
|
|
24
30
|
|
|
25
31
|
from .._version import __version__
|
|
26
32
|
from ._shared import console
|
|
@@ -46,9 +52,10 @@ class DefaultShellGroup(typer.core.TyperGroup):
|
|
|
46
52
|
if args:
|
|
47
53
|
first = args[0]
|
|
48
54
|
if first == "shell":
|
|
49
|
-
raise UsageError(
|
|
55
|
+
raise _click.exceptions.UsageError(
|
|
50
56
|
"`mindcode shell` 已移除;请改用 "
|
|
51
|
-
"`mindcode [PROMPT] [OPTIONS]`"
|
|
57
|
+
"`mindcode [PROMPT] [OPTIONS]`",
|
|
58
|
+
ctx,
|
|
52
59
|
)
|
|
53
60
|
if (
|
|
54
61
|
first in PUBLIC_SUBCOMMANDS
|
|
@@ -86,15 +86,22 @@ def run(
|
|
|
86
86
|
"--benchmark-root",
|
|
87
87
|
help="benchmark case 根目录",
|
|
88
88
|
),
|
|
89
|
-
max_steps: int = typer.Option(
|
|
89
|
+
max_steps: int | None = typer.Option(
|
|
90
|
+
None, "--max-steps", help="每 case 最大 step"
|
|
91
|
+
),
|
|
90
92
|
total_timeout: float | None = typer.Option(
|
|
91
|
-
|
|
93
|
+
None,
|
|
92
94
|
"--total-timeout",
|
|
93
|
-
help="每 case
|
|
95
|
+
help="每 case 总超时秒数,0 表示禁用",
|
|
94
96
|
),
|
|
95
97
|
) -> None:
|
|
96
98
|
run_id = f"{suite}-{time.strftime('%Y%m%d-%H%M%S')}"
|
|
97
99
|
try:
|
|
100
|
+
total_timeout_disabled = total_timeout == 0
|
|
101
|
+
if total_timeout is not None and total_timeout < 0:
|
|
102
|
+
raise ValueError("total timeout 必须为 0 或大于 0")
|
|
103
|
+
if total_timeout_disabled:
|
|
104
|
+
total_timeout = None
|
|
98
105
|
approval_override = (
|
|
99
106
|
ApprovalMode(approve_mode).value
|
|
100
107
|
if approve_mode is not None
|
|
@@ -106,6 +113,11 @@ def run(
|
|
|
106
113
|
approval_override=approval_override,
|
|
107
114
|
)
|
|
108
115
|
selected_approve_mode = resolved.approval_mode
|
|
116
|
+
benchmark_defaults = resolved.runtime_settings.benchmark
|
|
117
|
+
if max_steps is None:
|
|
118
|
+
max_steps = benchmark_defaults.max_steps
|
|
119
|
+
if total_timeout is None and not total_timeout_disabled:
|
|
120
|
+
total_timeout = benchmark_defaults.total_timeout_s
|
|
109
121
|
output_root = Path(output).expanduser()
|
|
110
122
|
run_output_dir = _run_output_dir(output_root, run_id)
|
|
111
123
|
run_config = BenchmarkRunConfig(
|
|
@@ -6,6 +6,7 @@ from pathlib import Path
|
|
|
6
6
|
|
|
7
7
|
import typer
|
|
8
8
|
|
|
9
|
+
from ...config import BUILTIN_SETTINGS
|
|
9
10
|
from ...policy import ApprovalMode
|
|
10
11
|
from .._shared import console
|
|
11
12
|
from ..shell import run_shell
|
|
@@ -41,15 +42,25 @@ def chat(
|
|
|
41
42
|
False, "--no-stream", help="关闭流式输出"
|
|
42
43
|
),
|
|
43
44
|
max_steps: int | None = typer.Option(
|
|
44
|
-
None,
|
|
45
|
+
None,
|
|
46
|
+
"--max-steps",
|
|
47
|
+
help=f"最大 step 数(默认 {BUILTIN_SETTINGS.one_shot.max_steps})",
|
|
45
48
|
),
|
|
46
49
|
step_timeout: float | None = typer.Option(
|
|
47
50
|
None,
|
|
48
51
|
"--step-timeout",
|
|
49
|
-
help=
|
|
52
|
+
help=(
|
|
53
|
+
"单步超时秒数(默认 "
|
|
54
|
+
f"{BUILTIN_SETTINGS.one_shot.step_timeout_s:g},必须大于 0)"
|
|
55
|
+
),
|
|
50
56
|
),
|
|
51
57
|
total_timeout: float | None = typer.Option(
|
|
52
|
-
None,
|
|
58
|
+
None,
|
|
59
|
+
"--total-timeout",
|
|
60
|
+
help=(
|
|
61
|
+
"总超时秒数,0 表示禁用(默认 "
|
|
62
|
+
f"{BUILTIN_SETTINGS.one_shot.total_timeout_s:g})"
|
|
63
|
+
),
|
|
53
64
|
),
|
|
54
65
|
) -> None:
|
|
55
66
|
"""发送单条消息,不进入交互模式。"""
|
|
@@ -78,13 +89,32 @@ def chat(
|
|
|
78
89
|
resume=resume,
|
|
79
90
|
stream=not no_stream,
|
|
80
91
|
one_shot_prompt=message,
|
|
81
|
-
max_steps=
|
|
92
|
+
max_steps=(
|
|
93
|
+
max_steps
|
|
94
|
+
if max_steps is not None
|
|
95
|
+
else BUILTIN_SETTINGS.one_shot.max_steps
|
|
96
|
+
),
|
|
82
97
|
step_timeout_s=(
|
|
83
|
-
step_timeout
|
|
98
|
+
step_timeout
|
|
99
|
+
if step_timeout is not None
|
|
100
|
+
else BUILTIN_SETTINGS.one_shot.step_timeout_s
|
|
84
101
|
),
|
|
85
102
|
total_timeout_s=None
|
|
86
103
|
if total_timeout == 0
|
|
87
|
-
else (
|
|
104
|
+
else (
|
|
105
|
+
total_timeout
|
|
106
|
+
if total_timeout is not None
|
|
107
|
+
else BUILTIN_SETTINGS.one_shot.total_timeout_s
|
|
108
|
+
),
|
|
109
|
+
settings_defaults=frozenset(
|
|
110
|
+
name
|
|
111
|
+
for name, value in (
|
|
112
|
+
("max_steps", max_steps),
|
|
113
|
+
("step_timeout_s", step_timeout),
|
|
114
|
+
("total_timeout_s", total_timeout),
|
|
115
|
+
)
|
|
116
|
+
if value is None
|
|
117
|
+
),
|
|
88
118
|
)
|
|
89
119
|
except ValueError as exc:
|
|
90
120
|
console.print(f"[red]配置错误:[/red] {exc}")
|
|
@@ -6,6 +6,7 @@ from pathlib import Path
|
|
|
6
6
|
|
|
7
7
|
import typer
|
|
8
8
|
|
|
9
|
+
from ...config import BUILTIN_SETTINGS
|
|
9
10
|
from ...policy import ApprovalMode
|
|
10
11
|
from .._shared import console
|
|
11
12
|
from ..shell import run_shell
|
|
@@ -52,17 +53,25 @@ def shell(
|
|
|
52
53
|
help="使用 master/coder 委派模式(await_result)",
|
|
53
54
|
),
|
|
54
55
|
max_steps: int | None = typer.Option(
|
|
55
|
-
None,
|
|
56
|
+
None,
|
|
57
|
+
"--max-steps",
|
|
58
|
+
help=f"最大 step 数(默认 {BUILTIN_SETTINGS.one_shot.max_steps})",
|
|
56
59
|
),
|
|
57
60
|
step_timeout: float | None = typer.Option(
|
|
58
61
|
None,
|
|
59
62
|
"--step-timeout",
|
|
60
|
-
help=
|
|
63
|
+
help=(
|
|
64
|
+
"单步超时秒数(交互默认不限制;one-shot 默认 "
|
|
65
|
+
f"{BUILTIN_SETTINGS.one_shot.step_timeout_s:g},必须大于 0)"
|
|
66
|
+
),
|
|
61
67
|
),
|
|
62
68
|
total_timeout: float | None = typer.Option(
|
|
63
69
|
None,
|
|
64
70
|
"--total-timeout",
|
|
65
|
-
help=
|
|
71
|
+
help=(
|
|
72
|
+
"总超时秒数,0 表示禁用(交互默认禁用;一次性/子代理默认 "
|
|
73
|
+
f"{BUILTIN_SETTINGS.one_shot.total_timeout_s:g})"
|
|
74
|
+
),
|
|
66
75
|
),
|
|
67
76
|
) -> None:
|
|
68
77
|
"""启动顶层 `mindcode` 交互或 one-shot 模式。"""
|
|
@@ -95,7 +104,9 @@ def shell(
|
|
|
95
104
|
selected_total_timeout = None
|
|
96
105
|
else:
|
|
97
106
|
selected_total_timeout = (
|
|
98
|
-
total_timeout
|
|
107
|
+
total_timeout
|
|
108
|
+
if total_timeout is not None
|
|
109
|
+
else BUILTIN_SETTINGS.one_shot.total_timeout_s
|
|
99
110
|
)
|
|
100
111
|
|
|
101
112
|
try:
|
|
@@ -109,13 +120,34 @@ def shell(
|
|
|
109
120
|
stream=not no_stream,
|
|
110
121
|
subagents=subagents,
|
|
111
122
|
one_shot_prompt=prompt,
|
|
112
|
-
max_steps=
|
|
123
|
+
max_steps=(
|
|
124
|
+
max_steps
|
|
125
|
+
if max_steps is not None
|
|
126
|
+
else (
|
|
127
|
+
BUILTIN_SETTINGS.interactive.max_steps
|
|
128
|
+
if prompt is None
|
|
129
|
+
else BUILTIN_SETTINGS.one_shot.max_steps
|
|
130
|
+
)
|
|
131
|
+
),
|
|
113
132
|
step_timeout_s=(
|
|
114
133
|
step_timeout
|
|
115
134
|
if step_timeout is not None
|
|
116
|
-
else (
|
|
135
|
+
else (
|
|
136
|
+
BUILTIN_SETTINGS.interactive.step_timeout_s
|
|
137
|
+
if prompt is None
|
|
138
|
+
else BUILTIN_SETTINGS.one_shot.step_timeout_s
|
|
139
|
+
)
|
|
117
140
|
),
|
|
118
141
|
total_timeout_s=selected_total_timeout,
|
|
142
|
+
settings_defaults=frozenset(
|
|
143
|
+
name
|
|
144
|
+
for name, value in (
|
|
145
|
+
("max_steps", max_steps),
|
|
146
|
+
("step_timeout_s", step_timeout),
|
|
147
|
+
("total_timeout_s", total_timeout),
|
|
148
|
+
)
|
|
149
|
+
if value is None
|
|
150
|
+
),
|
|
119
151
|
)
|
|
120
152
|
except ValueError as exc:
|
|
121
153
|
console.print(f"[red]配置错误:[/red] {exc}")
|
|
@@ -22,7 +22,7 @@ from mindagent.core import (
|
|
|
22
22
|
from mindagent.tools import MemoryReadTool
|
|
23
23
|
from rich.table import Table
|
|
24
24
|
|
|
25
|
-
from ...config import ConfigStore
|
|
25
|
+
from ...config import BUILTIN_SETTINGS, ConfigStore
|
|
26
26
|
from ...policy import ApprovalMode
|
|
27
27
|
from ...runtime import build_runtime
|
|
28
28
|
from ...tasking import TaskEventRecorder, TaskStatus, TaskStore
|
|
@@ -44,16 +44,16 @@ def run_task(
|
|
|
44
44
|
case_sensitive=False,
|
|
45
45
|
help="审批模式",
|
|
46
46
|
),
|
|
47
|
-
max_steps: int = typer.Option(
|
|
47
|
+
max_steps: int | None = typer.Option(None, "--max-steps", help="最大 step 数"),
|
|
48
48
|
step_timeout: float | None = typer.Option(
|
|
49
|
-
|
|
49
|
+
None,
|
|
50
50
|
"--step-timeout",
|
|
51
51
|
help="单步超时秒数(默认 300,必须大于 0)",
|
|
52
52
|
),
|
|
53
53
|
total_timeout: float | None = typer.Option(
|
|
54
|
-
|
|
54
|
+
None,
|
|
55
55
|
"--total-timeout",
|
|
56
|
-
help="
|
|
56
|
+
help="总超时秒数,0 表示禁用",
|
|
57
57
|
),
|
|
58
58
|
allow_dangerous: bool = typer.Option(
|
|
59
59
|
False,
|
|
@@ -70,6 +70,12 @@ def run_task(
|
|
|
70
70
|
if step_timeout is not None and step_timeout <= 0:
|
|
71
71
|
console.print("[red]无效 step timeout:[/red] 必须大于 0")
|
|
72
72
|
raise typer.Exit(2)
|
|
73
|
+
total_timeout_disabled = total_timeout == 0
|
|
74
|
+
if total_timeout is not None and total_timeout < 0:
|
|
75
|
+
console.print("[red]无效 total timeout:[/red] 必须为 0 或大于 0")
|
|
76
|
+
raise typer.Exit(2)
|
|
77
|
+
if total_timeout_disabled:
|
|
78
|
+
total_timeout = None
|
|
73
79
|
|
|
74
80
|
ws = Path(workspace).expanduser().resolve()
|
|
75
81
|
if not ws.is_dir():
|
|
@@ -87,6 +93,13 @@ def run_task(
|
|
|
87
93
|
raise typer.Exit(2)
|
|
88
94
|
model_name = provider_config.model
|
|
89
95
|
selected_approve_mode = ApprovalMode(provider_config.approval_mode)
|
|
96
|
+
task_defaults = provider_config.runtime_settings.task
|
|
97
|
+
if max_steps is None:
|
|
98
|
+
max_steps = task_defaults.max_steps
|
|
99
|
+
if step_timeout is None:
|
|
100
|
+
step_timeout = task_defaults.step_timeout_s
|
|
101
|
+
if total_timeout is None and not total_timeout_disabled:
|
|
102
|
+
total_timeout = task_defaults.total_timeout_s
|
|
90
103
|
store = TaskStore()
|
|
91
104
|
record = store.create(
|
|
92
105
|
goal=goal,
|
|
@@ -96,6 +109,7 @@ def run_task(
|
|
|
96
109
|
approve_mode=selected_approve_mode.value,
|
|
97
110
|
background=background,
|
|
98
111
|
max_steps=max_steps,
|
|
112
|
+
step_timeout_s=step_timeout,
|
|
99
113
|
total_timeout_s=total_timeout,
|
|
100
114
|
allow_dangerous=allow_dangerous,
|
|
101
115
|
)
|
|
@@ -163,7 +177,9 @@ def task_worker(task_id: str) -> None:
|
|
|
163
177
|
model=record.model or "",
|
|
164
178
|
approve_mode=ApprovalMode(record.approve_mode),
|
|
165
179
|
max_steps=record.max_steps,
|
|
166
|
-
step_timeout=getattr(
|
|
180
|
+
step_timeout=getattr(
|
|
181
|
+
record, "step_timeout_s", BUILTIN_SETTINGS.task.step_timeout_s
|
|
182
|
+
),
|
|
167
183
|
total_timeout=record.total_timeout_s,
|
|
168
184
|
allow_dangerous=record.allow_dangerous,
|
|
169
185
|
)
|
|
@@ -199,7 +215,9 @@ def resume_task(
|
|
|
199
215
|
model=record.model or "",
|
|
200
216
|
approve_mode=ApprovalMode(record.approve_mode),
|
|
201
217
|
max_steps=record.max_steps,
|
|
202
|
-
step_timeout=getattr(
|
|
218
|
+
step_timeout=getattr(
|
|
219
|
+
record, "step_timeout_s", BUILTIN_SETTINGS.task.step_timeout_s
|
|
220
|
+
),
|
|
203
221
|
total_timeout=record.total_timeout_s,
|
|
204
222
|
allow_dangerous=record.allow_dangerous,
|
|
205
223
|
resume=True,
|
|
@@ -225,7 +243,7 @@ async def _run_task(
|
|
|
225
243
|
max_steps: int,
|
|
226
244
|
total_timeout: float | None,
|
|
227
245
|
allow_dangerous: bool,
|
|
228
|
-
step_timeout: float | None =
|
|
246
|
+
step_timeout: float | None = BUILTIN_SETTINGS.task.step_timeout_s,
|
|
229
247
|
resume: bool = False,
|
|
230
248
|
user_input: str | None = None,
|
|
231
249
|
) -> bool:
|
|
@@ -34,6 +34,7 @@ class InputPipeline:
|
|
|
34
34
|
def __init__(self) -> None:
|
|
35
35
|
self._seq = 0
|
|
36
36
|
self._pending: UserInput | None = None
|
|
37
|
+
self._pending_items: list[str] = []
|
|
37
38
|
|
|
38
39
|
def has_pending(self) -> bool:
|
|
39
40
|
return self._pending is not None
|
|
@@ -49,6 +50,12 @@ class InputPipeline:
|
|
|
49
50
|
"""派生显示文本:合并文本单卡,不多卡不丢卡。"""
|
|
50
51
|
return self.peek_text()
|
|
51
52
|
|
|
53
|
+
def display_items(self) -> list[str] | None:
|
|
54
|
+
"""Independent pending texts for TUI cards, in FIFO order."""
|
|
55
|
+
if self._pending is None:
|
|
56
|
+
return None
|
|
57
|
+
return list(self._pending_items) or [self._pending.text]
|
|
58
|
+
|
|
52
59
|
def display_status(self) -> str:
|
|
53
60
|
"""strip 状态:appending 显示发送中,其余显示等待中。"""
|
|
54
61
|
if self._pending is not None and self._pending.exec == "appending":
|
|
@@ -59,9 +66,11 @@ class InputPipeline:
|
|
|
59
66
|
"""兼容旧测试的整包赋值;None 表示清空。"""
|
|
60
67
|
if text is None:
|
|
61
68
|
self._pending = None
|
|
69
|
+
self._pending_items = []
|
|
62
70
|
return
|
|
63
71
|
self._seq += 1
|
|
64
72
|
self._pending = UserInput(id=self._seq, text=text, kind=kind)
|
|
73
|
+
self._pending_items = [text]
|
|
65
74
|
|
|
66
75
|
def stage(self, text: str, kind: str = "normal") -> str:
|
|
67
76
|
"""忙时追问暂存:无 pending 则建卡,有则 ``\\n\\n`` 并入同一对象。"""
|
|
@@ -72,7 +81,9 @@ class InputPipeline:
|
|
|
72
81
|
if self._pending is None:
|
|
73
82
|
self._seq += 1
|
|
74
83
|
self._pending = UserInput(id=self._seq, text=text, kind=kind)
|
|
84
|
+
self._pending_items = [text]
|
|
75
85
|
elif text not in (None, ""):
|
|
86
|
+
self._pending_items.append(text)
|
|
76
87
|
if self._pending.text:
|
|
77
88
|
self._pending.text = self._pending.text + "\n\n" + text
|
|
78
89
|
else:
|
|
@@ -95,6 +106,7 @@ class InputPipeline:
|
|
|
95
106
|
self._pending.exec = "accepted"
|
|
96
107
|
text = self._pending.text
|
|
97
108
|
self._pending = None
|
|
109
|
+
self._pending_items = []
|
|
98
110
|
return text
|
|
99
111
|
|
|
100
112
|
def discard(self) -> str | None:
|
|
@@ -103,6 +115,7 @@ class InputPipeline:
|
|
|
103
115
|
return None
|
|
104
116
|
text = self._pending.text
|
|
105
117
|
self._pending = None
|
|
118
|
+
self._pending_items = []
|
|
106
119
|
return text
|
|
107
120
|
|
|
108
121
|
|