feat(sdk): support session permission commands

This commit is contained in:
LogosVeritas committed 2026-10-25 21:55:27 +08:00
1 parent 5cdd2aebed
commit 83a0867416
35 files changed
+737 -77

No files matched your search

+5 -2
View File
@@ -9,8 +9,11 @@
en: 4b79fd6576e2759d
zh: 6258540acaf4bc37
/python-sdk-example/compact-a-full-sdk-session:
en: 0bada22014bf5e6c
zh: 4308ea1a14b48f57
en: ad4d67225570ed01
zh: 0d59f6dac40129d6
/python-sdk-example/change-session-permissions:
en: c1162e41cd57dec4
zh: 96f608fa25e26c77
/python-sdk-example/add-plugins:
en: 5e1e30dac13f6c9e
zh: 0af98ebb75c88f43
+14
View File
@@ -42,6 +42,20 @@ python python/sdk/examples/compact.py \
The script consumes notifications and prints the command id and outcome separately from the model's responses. A failed turn or command stops the example. See [Query and response](../README.md#query-and-response) for completion and error behavior; `minimal.py` retains its `run()` entry and does not enable command execution.
<a id="change-session-permissions"></a>
## Change Session permissions
The [permission example](permission.py) starts the full `sdk` profile with a `workspace-write` default, sends `/permission <preset>`, then submits an ordinary query on the same Session. Supply the preset explicitly:
```sh
python python/sdk/examples/permission.py \
--dsh-home /absolute/path/to/permission-dsh-home \
--workspace /absolute/path/to/disposable-workspace \
--preset read-only
```
The command changes the Session's policy while the printed launch setting remains `workspace-write`. The later query uses the Session's current policy. Bare `/permission` is ordinary text, and unknown presets produce a command error. See [Query and response](../README.md#query-and-response) for result and state semantics.
## Add plugins
Use the runtime wheel's `dsh` command against the same explicit home for persistent profile changes:
+14
View File
@@ -42,6 +42,20 @@ python python/sdk/examples/compact.py \
脚本消费通知,并将命令 id 和结果与模型回复分开打印。轮次或命令失败时,示例会停止。完成和错误行为见[查询与接收](../README.zh.md#query-and-response);`minimal.py` 保留其 `run()` 入口,不启用命令执行。
<a id="change-session-permissions"></a>
## 修改 Session 权限
[权限示例](permission.py)以 `workspace-write` 为默认值启动完整 `sdk` profile,发送 `/permission <preset>`,随后在同一个 Session 中提交普通 query。请显式指定 preset:
```sh
python python/sdk/examples/permission.py \
--dsh-home /absolute/path/to/permission-dsh-home \
--workspace /absolute/path/to/disposable-workspace \
--preset read-only
```
命令会修改 Session 的策略,而打印出的启动设置仍为 `workspace-write`。后续 query 使用 Session 的当前策略。裸 `/permission` 是普通文本,未知 preset 返回命令错误。结果和状态语义见[查询与接收](../README.zh.md#query-and-response)。
## 添加插件
对同一个显式 home 使用运行时 wheel 包提供的 `dsh` 命令,以进行持久 profile 变更:
+58
View File
@@ -0,0 +1,58 @@
#!/usr/bin/env python3
"""Select a full SDK Session's permission preset and continue without resetting it."""
from __future__ import annotations
import argparse
import os
from contextlib import closing
from pathlib import Path
from deepseek_harness import CommandExecution, DeepSeekHarness, RunResult, Session
def receive(session: Session) -> None:
"""Print a command or activity result and stop on a reported failure."""
with closing(session.receive_response()) as responses:
for result in responses:
if isinstance(result, CommandExecution):
print(f"Command {result.command_id}: {result.result.kind}")
if result.result.kind == "error":
raise RuntimeError(result.result.text)
if result.result.text is not None:
print(result.result.text)
elif isinstance(result, RunResult):
if result.finish_reason != "completed":
raise RuntimeError(f"Agent turn ended with {result.finish_reason}")
print(result.final_response)
def main() -> None:
"""Send an explicit permission command, then query the same Session."""
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--dsh-home", required=True, type=Path)
parser.add_argument("--workspace", type=Path, default=Path.cwd())
parser.add_argument("--preset", required=True)
parser.add_argument("--provider", default="deepseek-official")
parser.add_argument("--model", default=os.environ.get("DSH_MODEL", "deepseek-v4-flash"))
args = parser.parse_args()
if not args.preset.strip():
parser.error("--preset must be non-blank")
with DeepSeekHarness(
dsh_home=str(args.dsh_home.resolve()), cwd=str(args.workspace.resolve()),
profile="sdk", provider=args.provider, model=args.model,
env={"DSH_PERMISSION_MODE": "workspace-write"},
) as harness:
session = harness.start_session()
session.query("Summarize the current file and approval policy in one sentence. Do not use tools.")
receive(session)
session.query(f"/permission {args.preset}")
receive(session)
print(f"Launch setting: {harness.config.env['DSH_PERMISSION_MODE']}")
session.query("Summarize the current file and approval policy again. Do not use tools.")
receive(session)
if __name__ == "__main__":
main()