mindcode 0.5.3__tar.gz → 0.5.5__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.3 → mindcode-0.5.5}/PKG-INFO +6 -6
- {mindcode-0.5.3 → mindcode-0.5.5}/README.md +5 -5
- {mindcode-0.5.3 → mindcode-0.5.5}/mindcode/_version.py +1 -1
- {mindcode-0.5.3 → mindcode-0.5.5}/mindcode/acp_agent/__main__.py +20 -0
- {mindcode-0.5.3 → mindcode-0.5.5}/mindcode/acp_agent/agent.py +24 -8
- {mindcode-0.5.3 → mindcode-0.5.5}/mindcode/approval.py +4 -4
- {mindcode-0.5.3 → mindcode-0.5.5}/mindcode/cli/commands/shell.py +4 -0
- {mindcode-0.5.3 → mindcode-0.5.5}/mindcode/cli/commands/task.py +4 -4
- {mindcode-0.5.3 → mindcode-0.5.5}/mindcode/cli/shell/repl.py +46 -5
- {mindcode-0.5.3 → mindcode-0.5.5}/mindcode/cli/shell/startup.py +0 -8
- {mindcode-0.5.3 → mindcode-0.5.5}/mindcode/cli/shell/tui.py +150 -30
- mindcode-0.5.5/mindcode/config/system.md +25 -0
- {mindcode-0.5.3 → mindcode-0.5.5}/mindcode/config.py +8 -3
- {mindcode-0.5.3 → mindcode-0.5.5}/mindcode/policy.py +7 -7
- {mindcode-0.5.3 → mindcode-0.5.5}/mindcode/render.py +17 -17
- {mindcode-0.5.3 → mindcode-0.5.5}/mindcode/runtime.py +103 -22
- {mindcode-0.5.3 → mindcode-0.5.5}/mindcode.egg-info/PKG-INFO +6 -6
- {mindcode-0.5.3 → mindcode-0.5.5}/mindcode.egg-info/SOURCES.txt +1 -0
- {mindcode-0.5.3 → mindcode-0.5.5}/pyproject.toml +2 -2
- {mindcode-0.5.3 → mindcode-0.5.5}/tests/test_acp_agent.py +83 -0
- {mindcode-0.5.3 → mindcode-0.5.5}/tests/test_approval.py +9 -9
- {mindcode-0.5.3 → mindcode-0.5.5}/tests/test_bench.py +3 -3
- {mindcode-0.5.3 → mindcode-0.5.5}/tests/test_diff_capture.py +2 -2
- {mindcode-0.5.3 → mindcode-0.5.5}/tests/test_exec_command_guard.py +1 -1
- {mindcode-0.5.3 → mindcode-0.5.5}/tests/test_external_file_approval.py +4 -4
- {mindcode-0.5.3 → mindcode-0.5.5}/tests/test_render_exec.py +3 -3
- {mindcode-0.5.3 → mindcode-0.5.5}/tests/test_shell_session.py +1 -1
- {mindcode-0.5.3 → mindcode-0.5.5}/tests/test_shell_tui.py +155 -24
- {mindcode-0.5.3 → mindcode-0.5.5}/tests/test_subagents.py +1 -1
- {mindcode-0.5.3 → mindcode-0.5.5}/tests/test_system_prompt.py +32 -0
- {mindcode-0.5.3 → mindcode-0.5.5}/mindcode/__init__.py +0 -0
- {mindcode-0.5.3 → mindcode-0.5.5}/mindcode/__main__.py +0 -0
- {mindcode-0.5.3 → mindcode-0.5.5}/mindcode/acp_agent/__init__.py +0 -0
- {mindcode-0.5.3 → mindcode-0.5.5}/mindcode/bench/__init__.py +0 -0
- {mindcode-0.5.3 → mindcode-0.5.5}/mindcode/bench/adapters/__init__.py +0 -0
- {mindcode-0.5.3 → mindcode-0.5.5}/mindcode/bench/adapters/base.py +0 -0
- {mindcode-0.5.3 → mindcode-0.5.5}/mindcode/bench/adapters/mini_bfcl.py +0 -0
- {mindcode-0.5.3 → mindcode-0.5.5}/mindcode/bench/adapters/mini_gaia.py +0 -0
- {mindcode-0.5.3 → mindcode-0.5.5}/mindcode/bench/adapters/mini_terminal.py +0 -0
- {mindcode-0.5.3 → mindcode-0.5.5}/mindcode/bench/adapters/swe_bench.py +0 -0
- {mindcode-0.5.3 → mindcode-0.5.5}/mindcode/bench/adapters/terminal_bench.py +0 -0
- {mindcode-0.5.3 → mindcode-0.5.5}/mindcode/bench/fake_tools.py +0 -0
- {mindcode-0.5.3 → mindcode-0.5.5}/mindcode/bench/jsonutil.py +0 -0
- {mindcode-0.5.3 → mindcode-0.5.5}/mindcode/bench/paths.py +0 -0
- {mindcode-0.5.3 → mindcode-0.5.5}/mindcode/bench/predictions.py +0 -0
- {mindcode-0.5.3 → mindcode-0.5.5}/mindcode/bench/report.py +0 -0
- {mindcode-0.5.3 → mindcode-0.5.5}/mindcode/bench/runner.py +0 -0
- {mindcode-0.5.3 → mindcode-0.5.5}/mindcode/bench/schema.py +0 -0
- {mindcode-0.5.3 → mindcode-0.5.5}/mindcode/bench/scorers/base.py +0 -0
- {mindcode-0.5.3 → mindcode-0.5.5}/mindcode/bench/scorers/composite.py +0 -0
- {mindcode-0.5.3 → mindcode-0.5.5}/mindcode/bench/scorers/exact.py +0 -0
- {mindcode-0.5.3 → mindcode-0.5.5}/mindcode/bench/scorers/json_call.py +0 -0
- {mindcode-0.5.3 → mindcode-0.5.5}/mindcode/bench/scorers/swe.py +0 -0
- {mindcode-0.5.3 → mindcode-0.5.5}/mindcode/bench/scorers/terminal.py +0 -0
- {mindcode-0.5.3 → mindcode-0.5.5}/mindcode/bench/scorers/terminal_bench.py +0 -0
- {mindcode-0.5.3 → mindcode-0.5.5}/mindcode/bench/trace.py +0 -0
- {mindcode-0.5.3 → mindcode-0.5.5}/mindcode/bench/workspace.py +0 -0
- {mindcode-0.5.3 → mindcode-0.5.5}/mindcode/cli/__init__.py +0 -0
- {mindcode-0.5.3 → mindcode-0.5.5}/mindcode/cli/_shared.py +0 -0
- {mindcode-0.5.3 → mindcode-0.5.5}/mindcode/cli/commands/__init__.py +0 -0
- {mindcode-0.5.3 → mindcode-0.5.5}/mindcode/cli/commands/bench.py +0 -0
- {mindcode-0.5.3 → mindcode-0.5.5}/mindcode/cli/commands/chat.py +0 -0
- {mindcode-0.5.3 → mindcode-0.5.5}/mindcode/cli/commands/config_cmd.py +0 -0
- {mindcode-0.5.3 → mindcode-0.5.5}/mindcode/cli/commands/remote.py +0 -0
- {mindcode-0.5.3 → mindcode-0.5.5}/mindcode/cli/commands/status.py +0 -0
- {mindcode-0.5.3 → mindcode-0.5.5}/mindcode/cli/commands/terminal.py +0 -0
- {mindcode-0.5.3 → mindcode-0.5.5}/mindcode/cli/shell/__init__.py +0 -0
- {mindcode-0.5.3 → mindcode-0.5.5}/mindcode/cli/shell/completion.py +0 -0
- {mindcode-0.5.3 → mindcode-0.5.5}/mindcode/cli/shell/input_pipeline.py +0 -0
- {mindcode-0.5.3 → mindcode-0.5.5}/mindcode/cli/shell/menu.py +0 -0
- {mindcode-0.5.3 → mindcode-0.5.5}/mindcode/cli/shell/slash.py +0 -0
- {mindcode-0.5.3 → mindcode-0.5.5}/mindcode/config/settings.toml +0 -0
- {mindcode-0.5.3 → mindcode-0.5.5}/mindcode/mcp.py +0 -0
- {mindcode-0.5.3 → mindcode-0.5.5}/mindcode/remote.py +0 -0
- {mindcode-0.5.3 → mindcode-0.5.5}/mindcode/skills.py +0 -0
- {mindcode-0.5.3 → mindcode-0.5.5}/mindcode/subagents.py +0 -0
- {mindcode-0.5.3 → mindcode-0.5.5}/mindcode/tasking.py +0 -0
- {mindcode-0.5.3 → mindcode-0.5.5}/mindcode/terminal/__init__.py +0 -0
- {mindcode-0.5.3 → mindcode-0.5.5}/mindcode/terminal/__main__.py +0 -0
- {mindcode-0.5.3 → mindcode-0.5.5}/mindcode/terminal_core.py +0 -0
- {mindcode-0.5.3 → mindcode-0.5.5}/mindcode.egg-info/dependency_links.txt +0 -0
- {mindcode-0.5.3 → mindcode-0.5.5}/mindcode.egg-info/entry_points.txt +0 -0
- {mindcode-0.5.3 → mindcode-0.5.5}/mindcode.egg-info/requires.txt +0 -0
- {mindcode-0.5.3 → mindcode-0.5.5}/mindcode.egg-info/top_level.txt +0 -0
- {mindcode-0.5.3 → mindcode-0.5.5}/setup.cfg +0 -0
- {mindcode-0.5.3 → mindcode-0.5.5}/tests/test_cli_commands.py +0 -0
- {mindcode-0.5.3 → mindcode-0.5.5}/tests/test_config.py +0 -0
- {mindcode-0.5.3 → mindcode-0.5.5}/tests/test_policy_choice.py +0 -0
- {mindcode-0.5.3 → mindcode-0.5.5}/tests/test_policy_timeout.py +0 -0
- {mindcode-0.5.3 → mindcode-0.5.5}/tests/test_provider_retry_defaults.py +0 -0
- {mindcode-0.5.3 → mindcode-0.5.5}/tests/test_reasoner_compat.py +0 -0
- {mindcode-0.5.3 → mindcode-0.5.5}/tests/test_remote.py +0 -0
- {mindcode-0.5.3 → mindcode-0.5.5}/tests/test_shell_channels.py +0 -0
- {mindcode-0.5.3 → mindcode-0.5.5}/tests/test_shell_completion.py +0 -0
- {mindcode-0.5.3 → mindcode-0.5.5}/tests/test_shell_interrupt.py +0 -0
- {mindcode-0.5.3 → mindcode-0.5.5}/tests/test_shell_slash_priority.py +0 -0
- {mindcode-0.5.3 → mindcode-0.5.5}/tests/test_skills_mcp.py +0 -0
- {mindcode-0.5.3 → mindcode-0.5.5}/tests/test_slash_resume.py +0 -0
- {mindcode-0.5.3 → mindcode-0.5.5}/tests/test_task_resume.py +0 -0
- {mindcode-0.5.3 → mindcode-0.5.5}/tests/test_tasking.py +0 -0
- {mindcode-0.5.3 → mindcode-0.5.5}/tests/test_terminal.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mindcode
|
|
3
|
-
Version: 0.5.
|
|
3
|
+
Version: 0.5.5
|
|
4
4
|
Summary: codex-style interactive coding agent on top of mindagent
|
|
5
5
|
Author: mindcode
|
|
6
6
|
License: MIT
|
|
@@ -35,7 +35,7 @@ Requires-Dist: prompt-toolkit>=3.0.30
|
|
|
35
35
|
- **可选 Subagent 模式**:`mindcode --subagents` 启用受限的 master/coder 协作;master 仅能委派,coder 才能访问 workspace
|
|
36
36
|
- 全屏 TUI:顶部 logo/会话信息、可滚动 Markdown 消息区、固定带边框多行输入框与状态栏
|
|
37
37
|
- 顶部信息栏显示本会话累计 token 消耗;provider 未返回 usage 时显示 `tokens —`
|
|
38
|
-
- Rich 富渲染:Markdown、彩色工具面板、
|
|
38
|
+
- Rich 富渲染:Markdown、彩色工具面板、edit diff 高亮和流式 LLM 输出
|
|
39
39
|
- TUI 内选择菜单:`/models`、`/permissions` 用方向键选择
|
|
40
40
|
- REPL 权限模式:`ask_user`(默认,写入和危险操作逐项询问)/ `full_accept`(接受权限上限内的操作风险)
|
|
41
41
|
- 会话持久化与恢复:基于 `LocalConversationStore`,存到 `~/.cache/mindcode/sessions/`
|
|
@@ -168,7 +168,7 @@ mindcode --subagents "审查当前改动并运行相关测试"
|
|
|
168
168
|
```bash
|
|
169
169
|
mindcode # 默认进 shell
|
|
170
170
|
› 在 workspace 创建 hello.txt 写入 hi
|
|
171
|
-
›
|
|
171
|
+
› edit (create) hello.txt [WRITE]
|
|
172
172
|
ask_user 将在执行前询问;也可切换到 /permissions full_accept
|
|
173
173
|
┌─ diff: hello.txt ──────────────────────┐
|
|
174
174
|
│ +hi │
|
|
@@ -333,15 +333,15 @@ mindcode/
|
|
|
333
333
|
持久化;运行结束会清理未消费的授权,不自动新增磁盘审计文件。
|
|
334
334
|
- 普通问答使用 `USER_INPUT_REQUIRED` 与 `submit_user_input()`;旧 `approve_run()`
|
|
335
335
|
因未绑定授权请求而被拒绝。
|
|
336
|
-
- `
|
|
336
|
+
- `command` 只限制 `cwd`,不会隔离子进程访问的文件。`sed`、`find`、`git diff`
|
|
337
337
|
等命令保守按写风险审批,具体范围见
|
|
338
338
|
[策略与权限](../../docs/guides/policies-and-approval.md)。
|
|
339
339
|
|
|
340
340
|
## 局限
|
|
341
341
|
|
|
342
|
-
- 不支持 `/undo`:`
|
|
342
|
+
- 不支持 `/undo`:`command` 的副作用可能不可逆,回滚复杂。
|
|
343
343
|
- 会话恢复只续聊 user/assistant 历史,不恢复 TaskState / Evidence / pending continuation。
|
|
344
344
|
- `task resume` 是重新观察 workspace 后创建新 Run,不会重放未确认的旧 Action。
|
|
345
345
|
- 后台任务目前依赖本机 PID;不提供跨主机监督或精确进程身份恢复。
|
|
346
|
-
- `bench run` 第一版仅支持串行(concurrency=1);`
|
|
346
|
+
- `bench run` 第一版仅支持串行(concurrency=1);`command` 无沙箱,
|
|
347
347
|
SWE-bench / Terminal-Bench 评测依赖本机对应的评测环境。
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
- **可选 Subagent 模式**:`mindcode --subagents` 启用受限的 master/coder 协作;master 仅能委派,coder 才能访问 workspace
|
|
20
20
|
- 全屏 TUI:顶部 logo/会话信息、可滚动 Markdown 消息区、固定带边框多行输入框与状态栏
|
|
21
21
|
- 顶部信息栏显示本会话累计 token 消耗;provider 未返回 usage 时显示 `tokens —`
|
|
22
|
-
- Rich 富渲染:Markdown、彩色工具面板、
|
|
22
|
+
- Rich 富渲染:Markdown、彩色工具面板、edit diff 高亮和流式 LLM 输出
|
|
23
23
|
- TUI 内选择菜单:`/models`、`/permissions` 用方向键选择
|
|
24
24
|
- REPL 权限模式:`ask_user`(默认,写入和危险操作逐项询问)/ `full_accept`(接受权限上限内的操作风险)
|
|
25
25
|
- 会话持久化与恢复:基于 `LocalConversationStore`,存到 `~/.cache/mindcode/sessions/`
|
|
@@ -152,7 +152,7 @@ mindcode --subagents "审查当前改动并运行相关测试"
|
|
|
152
152
|
```bash
|
|
153
153
|
mindcode # 默认进 shell
|
|
154
154
|
› 在 workspace 创建 hello.txt 写入 hi
|
|
155
|
-
›
|
|
155
|
+
› edit (create) hello.txt [WRITE]
|
|
156
156
|
ask_user 将在执行前询问;也可切换到 /permissions full_accept
|
|
157
157
|
┌─ diff: hello.txt ──────────────────────┐
|
|
158
158
|
│ +hi │
|
|
@@ -317,15 +317,15 @@ mindcode/
|
|
|
317
317
|
持久化;运行结束会清理未消费的授权,不自动新增磁盘审计文件。
|
|
318
318
|
- 普通问答使用 `USER_INPUT_REQUIRED` 与 `submit_user_input()`;旧 `approve_run()`
|
|
319
319
|
因未绑定授权请求而被拒绝。
|
|
320
|
-
- `
|
|
320
|
+
- `command` 只限制 `cwd`,不会隔离子进程访问的文件。`sed`、`find`、`git diff`
|
|
321
321
|
等命令保守按写风险审批,具体范围见
|
|
322
322
|
[策略与权限](../../docs/guides/policies-and-approval.md)。
|
|
323
323
|
|
|
324
324
|
## 局限
|
|
325
325
|
|
|
326
|
-
- 不支持 `/undo`:`
|
|
326
|
+
- 不支持 `/undo`:`command` 的副作用可能不可逆,回滚复杂。
|
|
327
327
|
- 会话恢复只续聊 user/assistant 历史,不恢复 TaskState / Evidence / pending continuation。
|
|
328
328
|
- `task resume` 是重新观察 workspace 后创建新 Run,不会重放未确认的旧 Action。
|
|
329
329
|
- 后台任务目前依赖本机 PID;不提供跨主机监督或精确进程身份恢复。
|
|
330
|
-
- `bench run` 第一版仅支持串行(concurrency=1);`
|
|
330
|
+
- `bench run` 第一版仅支持串行(concurrency=1);`command` 无沙箱,
|
|
331
331
|
SWE-bench / Terminal-Bench 评测依赖本机对应的评测环境。
|
|
@@ -14,6 +14,7 @@ from pathlib import Path
|
|
|
14
14
|
|
|
15
15
|
from acp import run_agent
|
|
16
16
|
|
|
17
|
+
from ..policy import ApprovalMode
|
|
17
18
|
from .agent import _AgentHarness
|
|
18
19
|
|
|
19
20
|
|
|
@@ -37,6 +38,23 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
37
38
|
default=None,
|
|
38
39
|
help="Default workspace used when a client connects without a cwd",
|
|
39
40
|
)
|
|
41
|
+
parser.add_argument(
|
|
42
|
+
"--approval-mode",
|
|
43
|
+
choices=[mode.value for mode in ApprovalMode],
|
|
44
|
+
default=None,
|
|
45
|
+
help=(
|
|
46
|
+
"Override the approval mode read from config.toml. This is a "
|
|
47
|
+
"host-side launch decision; ACP requests can never change it."
|
|
48
|
+
),
|
|
49
|
+
)
|
|
50
|
+
parser.add_argument(
|
|
51
|
+
"--allow-dangerous",
|
|
52
|
+
action="store_true",
|
|
53
|
+
help=(
|
|
54
|
+
"Allow ActionRisk.DANGEROUS tools in created sessions. Only for "
|
|
55
|
+
"trusted, attended hosts: it lifts the SessionPolicy ceiling."
|
|
56
|
+
),
|
|
57
|
+
)
|
|
40
58
|
parser.add_argument("--verbose", action="store_true", help="stderr debug logs")
|
|
41
59
|
args = parser.parse_args(argv)
|
|
42
60
|
|
|
@@ -49,6 +67,8 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
49
67
|
harness = _AgentHarness(
|
|
50
68
|
conn, # type: ignore[arg-type]
|
|
51
69
|
cwd=Path(args.cwd).expanduser() if args.cwd else None,
|
|
70
|
+
approval_mode=args.approval_mode,
|
|
71
|
+
allow_dangerous=args.allow_dangerous,
|
|
52
72
|
)
|
|
53
73
|
harness_holder.append(harness)
|
|
54
74
|
return harness
|
|
@@ -11,8 +11,10 @@ approval callback / event stream. Hard rules honoured here:
|
|
|
11
11
|
|
|
12
12
|
- stdout carries protocol frames only; every log line goes to stderr.
|
|
13
13
|
- one run per session at a time; a second ``session/prompt`` is rejected.
|
|
14
|
-
- provider/model/approval configuration
|
|
15
|
-
|
|
14
|
+
- provider/model/approval configuration comes from ``ConfigStore`` (fresh
|
|
15
|
+
``read()`` per session); an ACP *request* can never widen it. Only the host
|
|
16
|
+
that launched this process may override approval/session limits, through
|
|
17
|
+
explicit CLI flags (``--approval-mode``, ``--allow-dangerous``).
|
|
16
18
|
- unimplemented stable methods (``session/load``, ``session/set_mode``)
|
|
17
19
|
answer with a JSON-RPC error instead of pretending support.
|
|
18
20
|
"""
|
|
@@ -70,9 +72,9 @@ def _stop_reason(result: Any) -> str:
|
|
|
70
72
|
|
|
71
73
|
|
|
72
74
|
def _tool_kind(action_name: str) -> str:
|
|
73
|
-
if action_name.startswith("
|
|
75
|
+
if action_name.startswith("read"):
|
|
74
76
|
return "read"
|
|
75
|
-
if action_name in ("
|
|
77
|
+
if action_name in ("write", "edit"):
|
|
76
78
|
return "edit"
|
|
77
79
|
if action_name.startswith("exec_") or "command" in action_name:
|
|
78
80
|
return "execute"
|
|
@@ -121,12 +123,19 @@ class _AgentHarness:
|
|
|
121
123
|
config_store: ConfigStore | None = None,
|
|
122
124
|
cwd: Path | None = None,
|
|
123
125
|
subagents: bool = False,
|
|
126
|
+
approval_mode: str | None = None,
|
|
127
|
+
allow_dangerous: bool = False,
|
|
124
128
|
permission_timeout_s: float | None = None,
|
|
125
129
|
) -> None:
|
|
126
130
|
self._conn = conn
|
|
127
131
|
self._config_store = config_store or ConfigStore()
|
|
128
132
|
self._cwd = cwd or Path.cwd()
|
|
129
133
|
self._subagents = subagents
|
|
134
|
+
# Explicit launch-time policy, always from the *host* that started
|
|
135
|
+
# this process (CLI flags, i.e. the user's own settings) — never from
|
|
136
|
+
# an ACP request. ``None`` keeps the ConfigStore value.
|
|
137
|
+
self._approval_mode = approval_mode
|
|
138
|
+
self._allow_dangerous = allow_dangerous
|
|
130
139
|
self._permission_timeout_from_config = permission_timeout_s is None
|
|
131
140
|
if permission_timeout_s is None:
|
|
132
141
|
try:
|
|
@@ -186,10 +195,12 @@ class _AgentHarness:
|
|
|
186
195
|
store = LocalConversationStore(
|
|
187
196
|
self._config_store.root / "sessions"
|
|
188
197
|
)
|
|
198
|
+
effective_mode = self._approval_mode or resolved.approval_mode
|
|
199
|
+
effective_approval = ApprovalMode(effective_mode)
|
|
189
200
|
runtime = build_runtime(
|
|
190
201
|
workspace,
|
|
191
202
|
provider_config=resolved,
|
|
192
|
-
approve_mode=
|
|
203
|
+
approve_mode=effective_approval,
|
|
193
204
|
stream=True,
|
|
194
205
|
event_handler=None,
|
|
195
206
|
approval_callback=self._make_approval(resolved),
|
|
@@ -203,7 +214,7 @@ class _AgentHarness:
|
|
|
203
214
|
),
|
|
204
215
|
policy=SessionPolicy(
|
|
205
216
|
allow_write=True,
|
|
206
|
-
allow_dangerous=
|
|
217
|
+
allow_dangerous=self._allow_dangerous,
|
|
207
218
|
max_parallel_actions=1,
|
|
208
219
|
),
|
|
209
220
|
conversation_store=store,
|
|
@@ -216,12 +227,13 @@ class _AgentHarness:
|
|
|
216
227
|
session_id, session, runtime
|
|
217
228
|
)
|
|
218
229
|
logger.info(
|
|
219
|
-
"session %s created (workspace=%s provider=%s model=%s approval=%s)",
|
|
230
|
+
"session %s created (workspace=%s provider=%s model=%s approval=%s allow_dangerous=%s)",
|
|
220
231
|
session_id,
|
|
221
232
|
workspace,
|
|
222
233
|
resolved.name,
|
|
223
234
|
resolved.model,
|
|
224
|
-
|
|
235
|
+
effective_mode,
|
|
236
|
+
self._allow_dangerous,
|
|
225
237
|
)
|
|
226
238
|
return NewSessionResponse(session_id=session_id)
|
|
227
239
|
|
|
@@ -518,6 +530,8 @@ def build_harness(
|
|
|
518
530
|
config_store: ConfigStore | None = None,
|
|
519
531
|
cwd: Path | None = None,
|
|
520
532
|
subagents: bool = False,
|
|
533
|
+
approval_mode: str | None = None,
|
|
534
|
+
allow_dangerous: bool = False,
|
|
521
535
|
) -> _AgentHarness:
|
|
522
536
|
"""Factory used by ``__main__`` and tests."""
|
|
523
537
|
return _AgentHarness(
|
|
@@ -525,4 +539,6 @@ def build_harness(
|
|
|
525
539
|
config_store=config_store,
|
|
526
540
|
cwd=cwd,
|
|
527
541
|
subagents=subagents,
|
|
542
|
+
approval_mode=approval_mode,
|
|
543
|
+
allow_dangerous=allow_dangerous,
|
|
528
544
|
)
|
|
@@ -42,17 +42,17 @@ def describe_action(action: ActionRequest) -> str:
|
|
|
42
42
|
arguments = (
|
|
43
43
|
action.arguments if isinstance(action.arguments, dict) else {}
|
|
44
44
|
)
|
|
45
|
-
if name in ("
|
|
46
|
-
verb = "Edit" if name == "
|
|
45
|
+
if name in ("edit", "write"):
|
|
46
|
+
verb = "Edit" if name == "edit" else "Write"
|
|
47
47
|
path = _one_line(arguments.get("path", "?"))
|
|
48
48
|
return f"{verb} {path} [{risk_label(action.risk)}]"
|
|
49
|
-
if name == "
|
|
49
|
+
if name == "command":
|
|
50
50
|
command = arguments.get("command", "")
|
|
51
51
|
preview = _one_line(command) if isinstance(command, str) else ""
|
|
52
52
|
if len(preview) > 120:
|
|
53
53
|
preview = preview[:117] + "..."
|
|
54
54
|
return f"Command {preview} [{risk_label(action.risk)}]"
|
|
55
|
-
if name in ("
|
|
55
|
+
if name in ("read", "memory_read"):
|
|
56
56
|
path = _one_line(arguments.get("path", "?"))
|
|
57
57
|
return f"Read {path} [{risk_label(action.risk)}]"
|
|
58
58
|
payload = json.dumps(arguments, ensure_ascii=False, sort_keys=True)
|
|
@@ -47,6 +47,9 @@ def shell(
|
|
|
47
47
|
no_stream: bool = typer.Option(
|
|
48
48
|
False, "--no-stream", help="关闭流式输出"
|
|
49
49
|
),
|
|
50
|
+
no_markdown: bool = typer.Option(
|
|
51
|
+
False, "--no-markdown", help="关闭交互消息的 Markdown 渲染"
|
|
52
|
+
),
|
|
50
53
|
subagents: bool = typer.Option(
|
|
51
54
|
False,
|
|
52
55
|
"--subagents",
|
|
@@ -118,6 +121,7 @@ def shell(
|
|
|
118
121
|
session_id=session,
|
|
119
122
|
resume=resume,
|
|
120
123
|
stream=not no_stream,
|
|
124
|
+
markdown_enabled=False if no_markdown else None,
|
|
121
125
|
subagents=subagents,
|
|
122
126
|
one_shot_prompt=prompt,
|
|
123
127
|
max_steps=(
|
|
@@ -284,10 +284,10 @@ async def _run_task(
|
|
|
284
284
|
policy = SessionPolicy(
|
|
285
285
|
allowed_tools=frozenset(
|
|
286
286
|
{
|
|
287
|
-
"
|
|
288
|
-
"
|
|
289
|
-
"
|
|
290
|
-
"
|
|
287
|
+
"read",
|
|
288
|
+
"write",
|
|
289
|
+
"edit",
|
|
290
|
+
"command",
|
|
291
291
|
"memory_read",
|
|
292
292
|
}
|
|
293
293
|
),
|
|
@@ -204,10 +204,12 @@ class Shell:
|
|
|
204
204
|
max_steps: int = BUILTIN_SETTINGS.run.max_steps,
|
|
205
205
|
step_timeout_s: float | None = BUILTIN_SETTINGS.run.step_timeout_s,
|
|
206
206
|
total_timeout_s: float | None = BUILTIN_SETTINGS.run.total_timeout_s,
|
|
207
|
+
markdown_enabled: bool | None = None,
|
|
207
208
|
):
|
|
208
209
|
self.workspace = workspace
|
|
209
210
|
self.approve_mode = approve_mode
|
|
210
211
|
self.stream = stream
|
|
212
|
+
self.markdown_enabled = markdown_enabled
|
|
211
213
|
self.resume_id = resume
|
|
212
214
|
self.explicit_session_id = session_id
|
|
213
215
|
|
|
@@ -359,7 +361,9 @@ class Shell:
|
|
|
359
361
|
on_submit=self._submit_from_tui,
|
|
360
362
|
on_exit=self._request_exit,
|
|
361
363
|
on_interrupt=self._interrupt_current,
|
|
364
|
+
on_push_pending=self._push_pending_now,
|
|
362
365
|
status_provider=self._status_text,
|
|
366
|
+
markdown_enabled=self.markdown_enabled,
|
|
363
367
|
)
|
|
364
368
|
self.renderer.attach_transcript(self._tui.transcript)
|
|
365
369
|
self.state.choose_option = self._tui.choose
|
|
@@ -716,6 +720,42 @@ class Shell:
|
|
|
716
720
|
except Exception as exc:
|
|
717
721
|
self._note(f"未能停止:{exc}")
|
|
718
722
|
|
|
723
|
+
async def _push_pending_now(self) -> None:
|
|
724
|
+
"""Ctrl-Enter: interrupt the active run and dispatch the oldest pending input."""
|
|
725
|
+
session = getattr(self.state, "session", None)
|
|
726
|
+
if not self._busy or session is None:
|
|
727
|
+
return
|
|
728
|
+
pipe = self._pipeline()
|
|
729
|
+
first = pipe.peek_text()
|
|
730
|
+
if first is None:
|
|
731
|
+
return
|
|
732
|
+
# Move the staged item ahead of the queue; the normal process loop will
|
|
733
|
+
# dispatch it after the current run is cancelled.
|
|
734
|
+
queued: list[str] = []
|
|
735
|
+
while True:
|
|
736
|
+
try:
|
|
737
|
+
item = self._queue.get_nowait()
|
|
738
|
+
except asyncio.QueueEmpty:
|
|
739
|
+
break
|
|
740
|
+
if isinstance(item, str):
|
|
741
|
+
queued.append(item)
|
|
742
|
+
else:
|
|
743
|
+
self._queue.put_nowait(item)
|
|
744
|
+
break
|
|
745
|
+
pipe.discard()
|
|
746
|
+
self._queue.put_nowait(first)
|
|
747
|
+
for item in queued:
|
|
748
|
+
self._queue.put_nowait(item)
|
|
749
|
+
self._sync_pending_display()
|
|
750
|
+
active_id = getattr(session, "active_run_id", None) or getattr(
|
|
751
|
+
self.state, "last_run_id", None
|
|
752
|
+
)
|
|
753
|
+
if active_id:
|
|
754
|
+
try:
|
|
755
|
+
await session.cancel_run(active_id)
|
|
756
|
+
except Exception as exc:
|
|
757
|
+
self._note(f"未能立即推送:{exc}")
|
|
758
|
+
|
|
719
759
|
async def _process_loop(self) -> None:
|
|
720
760
|
"""轮间调度:先排空 slash 优先队,再跑一条普通消息。
|
|
721
761
|
|
|
@@ -845,10 +885,6 @@ class Shell:
|
|
|
845
885
|
def _print_banner(self) -> None:
|
|
846
886
|
auth = self.config.auth.get(self.state.active_provider)
|
|
847
887
|
if self._tui is not None:
|
|
848
|
-
self._tui.transcript.add_system(
|
|
849
|
-
"已就绪。输入消息开始对话;/help 查看命令;/exit 退出;"
|
|
850
|
-
"运行中 Ctrl-C 取回消息继续编辑,Ctrl-D 退出。"
|
|
851
|
-
)
|
|
852
888
|
if self.state.history:
|
|
853
889
|
self._tui.transcript.add_system(
|
|
854
890
|
f"已恢复 {len(self.state.history)} 条历史消息。"
|
|
@@ -1199,10 +1235,14 @@ def run_shell(
|
|
|
1199
1235
|
max_steps: int = BUILTIN_SETTINGS.run.max_steps,
|
|
1200
1236
|
step_timeout_s: float | None = BUILTIN_SETTINGS.run.step_timeout_s,
|
|
1201
1237
|
total_timeout_s: float | None = BUILTIN_SETTINGS.run.total_timeout_s,
|
|
1238
|
+
markdown_enabled: bool | None = None,
|
|
1202
1239
|
settings_defaults: frozenset[str] = frozenset(),
|
|
1203
1240
|
) -> int:
|
|
1204
1241
|
"""Synchronous entry: build Shell and run it (or one-shot)."""
|
|
1205
|
-
|
|
1242
|
+
config_snapshot = ConfigStore().read()
|
|
1243
|
+
configured = config_snapshot.runtime_settings
|
|
1244
|
+
if markdown_enabled is None:
|
|
1245
|
+
markdown_enabled = config_snapshot.settings.markdown_enabled
|
|
1206
1246
|
profile = (
|
|
1207
1247
|
configured.one_shot
|
|
1208
1248
|
if one_shot_prompt is not None or subagents
|
|
@@ -1226,6 +1266,7 @@ def run_shell(
|
|
|
1226
1266
|
max_steps=max_steps,
|
|
1227
1267
|
step_timeout_s=step_timeout_s,
|
|
1228
1268
|
total_timeout_s=total_timeout_s,
|
|
1269
|
+
markdown_enabled=markdown_enabled,
|
|
1229
1270
|
)
|
|
1230
1271
|
|
|
1231
1272
|
if one_shot_prompt:
|
|
@@ -41,14 +41,6 @@ def build_welcome_banner(
|
|
|
41
41
|
"配置后重新启动"
|
|
42
42
|
)
|
|
43
43
|
)
|
|
44
|
-
lines.append(Text(""))
|
|
45
|
-
lines.append(
|
|
46
|
-
Text.from_markup(
|
|
47
|
-
"[dim]输入消息开始对话;[cyan]/help[/cyan] 查看命令;"
|
|
48
|
-
"[cyan]/exit[/cyan] 退出;运行中 Ctrl-C 取回消息继续编辑,"
|
|
49
|
-
"Ctrl-D 退出[/dim]"
|
|
50
|
-
)
|
|
51
|
-
)
|
|
52
44
|
return Panel(
|
|
53
45
|
Group(*lines),
|
|
54
46
|
border_style="cyan",
|