Merge master current-directory support into subagent activations

Integrate the observed master checkpoint 7ab215e23e5d977aae7eb5d4fb91782d39969dbb without rewriting the earlier published checkpoints. Master adds current-directory events, directory-aware consumers, and retained Git worktrees; its old subagent startup paths overlapped the unified activation implementation.

Resolve directory selection within managed startup after acquiring parent ownership. Preserve the original Session origin while setting each new local child directory, retain persisted directory state on cold resume, and pass a required absolute cwd to external providers. Keep unified local/external ownership, full completion notices, parked-input host completion, SDK wait/error/shutdown behavior, and ACP cancellation semantics. Retain removal of the old in-process driver and continuation managers.

Combine SDK wait with the new directory requests, preserve the new master directory/worktree fixtures, regenerate derived catalogs from the combined sources, and refresh only the four stale prompt/schema sidecars. Preserve every protected historical schema, archived note, catalog-v2 acknowledgement, and recorded predecessor input.

Validation: full build; 59 CI static gates; 855 subagent tests with 100% coverage on changed core/external sources; affected consumer suites including 119 repaired-fixture tests; SDK 11 and headless 16 recorded scenarios; two local headless expected cases; six built nested SDK/ACP cases; lint, strict cast inventory, workspace constraints, and whitespace checks. Native Windows worker diagnostics remain additive and require the next CI run to classify the original crash.
This commit is contained in:
Tianyi Cui committed 2026-10-06 10:52:35 +08:00
commit a3cb678875
1125 files changed
+31903 -6278

No files matched your search

+3 -1
View File
@@ -160,6 +160,8 @@
"@deepseek-ai/dsh-workflow-ptc": "workspace:*",
"@deepseek-ai/dsh-otel": "workspace:*",
"@deepseek-ai/dsh-workspace": "workspace:*",
"@deepseek-ai/schemastery": "workspace:~"
"@deepseek-ai/schemastery": "workspace:~",
"@deepseek-ai/dsh-tool-working-directory": "workspace:*",
"@deepseek-ai/dsh-working-directory": "workspace:*"
}
}
+2 -2
View File
@@ -12,5 +12,5 @@
en: d32ee602f121e6af
zh: fdea4487688e5774
/deepseek-harness-python-sdk/results-and-notifications:
en: 2658424dd12e1ad9
zh: 110d72f5bd836caf
en: 173d2dc1ce63fe2d
zh: 5354e18c59f20878
+2
View File
@@ -70,3 +70,5 @@ The shipped `sdk-minimal` profile is a standalone explicit tree rather than an o
The selected home stores profiles, plugins, and every profile-owned durable resource. The full `sdk` profile uses its credentials, settings, and session stores; `sdk-minimal` uses only its JSONL session store. Use a fresh home when those resources must be isolated, and a fresh session id for independent work. Reusing both a harness and session id continues the durable conversation and session-owned resources.
See the [Python tutorial](../../docs/user/guide/python-sdk.md), [runnable example](examples/README.md), and [runtime wheel reference](../sdk-runtime/README.md).
`Session.get_working_directory()` reads the effective working directory; `set_working_directory(path)` changes it and returns the validated absolute path. Both create an unknown Session without running a model turn. Relative paths use that Session's current directory. Origin metadata, permissions, and existing processes remain unchanged. The low-level client provides the same methods with a leading `session_id` argument.
+2
View File
@@ -70,3 +70,5 @@ with DeepSeekHarness(
所选 home 保存 profile、插件与每个 profile 自有的持久资源。完整 `sdk` profile 使用其中的凭据、设置与会话存储;`sdk-minimal` 只使用自己的 JSONL 会话存储。需要隔离这些资源时应使用新的 home;独立工作应使用新的会话 ID。同时复用 harness 与会话 ID 会延续持久对话和会话资源。
另见 [Python 教程](../../docs/user/guide/python-sdk.zh.md)、[可运行示例](examples/README.zh.md) 和 [运行时 wheel 包参考](../sdk-runtime/README.zh.md)。
`Session.get_working_directory()` 读取有效工作目录;`set_working_directory(path)` 修改目录并返回校验后的绝对路径。两者都可创建未知 Session,而不运行模型轮次。相对路径以该 Session 当前目录解析。起始目录元数据、权限与已有进程保持不变。底层客户端提供同名方法,并以 `session_id` 作为首个参数。
+10
View File
@@ -136,6 +136,16 @@ class Session:
self.harness = harness
self.id = session_id
def get_working_directory(self) -> str:
"""Read the effective directory, recovering a missing directory to the Session origin."""
self.harness.start()
return self.harness.client.get_working_directory(self.id)
def set_working_directory(self, path: str) -> str:
"""Change the effective directory while existing processes retain their own directories."""
self.harness.start()
return self.harness.client.set_working_directory(self.id, path)
def run(
self,
input: str | list[JsonObject],
+22
View File
@@ -188,6 +188,24 @@ class HarnessClient:
)
return response.messageId
def get_working_directory(self, session_id: str) -> str:
"""Read the effective directory, recovering a missing directory to the Session origin."""
response = self.request(
"session/working-directory/get",
{"sessionId": session_id},
response_model=_WorkingDirectoryResponse,
)
return response.cwd
def set_working_directory(self, session_id: str, path: str) -> str:
"""Enter an absolute or Session-relative directory without changing origin or permissions."""
response = self.request(
"session/working-directory/set",
{"sessionId": session_id, "path": path},
response_model=_WorkingDirectoryResponse,
)
return response.cwd
def request(
self,
method: str,
@@ -581,6 +599,10 @@ class _SessionPromptResponse(BaseModel):
messageId: str
class _WorkingDirectoryResponse(BaseModel):
cwd: str
class _ShutdownResponse(BaseModel):
pass
+2
View File
@@ -105,6 +105,7 @@ for line in sys.stdin:
"arguments": {"command": "git push --force"},
"isError": True,
"content": [{"type": "text", "text": "Error: blocked by policy"}],
"meta": {"cwd": "/selected", "path": "/selected/note.txt", "view": {"line": 3}},
"error": {
"name": "AutoReviewDeniedError",
"code": "AUTO_REVIEW_DENIED",
@@ -200,6 +201,7 @@ for line in sys.stdin:
]
ptc_events = [event for event in result.events if event["type"].startswith("tool/ptc-dispatch")]
assert [event["type"] for event in ptc_events] == ["tool/ptc-dispatch-start", "tool/ptc-dispatch"]
assert ptc_events[-1]["data"]["meta"] == {"cwd": "/selected", "path": "/selected/note.txt", "view": {"line": 3}}
for event in ptc_events:
assert not {"description", "parameters", "schema"}.intersection(event["data"])
dumped_env = json.loads(env_dump.read_text())
+16
View File
@@ -259,6 +259,22 @@ def test_mock_model_serves_native_messages_events() -> None:
assert next(event["delta"]["text"] for event in events if event["type"] == "content_block_delta") == SMOKE["EXPECTED_TEXT"]
@pytest.mark.parametrize("directory", ["/fixture/workspace", r"C:\fixture\workspace"])
def test_minimal_snapshot_pins_quoted_working_directory(directory: str) -> None:
context = f"Current working directory: {json.dumps(directory)}."
other = json.dumps(r"C:\another\directory")
files = SMOKE["build_minimal_snapshot_files"]([{
"system": [],
"tools": [],
"messages": [{"role": "user", "content": [{"type": "text", "text": context + "\n" + other}]}],
}], Path(directory))
message = json.loads(files["model-visible.json"])[0]["messages"][0]
assert message == {
"role": "user",
"content": [{"type": "text", "text": 'Current working directory: "{{cwd}}".\n' + other}],
}
def test_advanced_snapshot_normalizes_catalog_child_creation_time() -> None:
value = {
"type": "subagent/catalog",
@@ -0,0 +1,54 @@
from __future__ import annotations
import sys
from pathlib import Path
import pytest
from pydantic import ValidationError
from deepseek_harness import DeepSeekHarness
def test_session_directories_are_independent_and_relative_paths_stay_on_the_wire(tmp_path: Path) -> None:
runtime = tmp_path / "directory_runtime.py"
runtime.write_text(
"""
import json
import os
import sys
directories = {}
origin = None
for line in sys.stdin:
frame = json.loads(line)
method = frame['method']
params = frame.get('params') or {}
if method == 'initialize':
origin = params['cwd']
result = {'serverInfo': {'name': 'directory-runtime', 'version': '1'}}
elif method == 'shutdown':
print(json.dumps({'jsonrpc': '2.0', 'id': frame['id'], 'result': {}}), flush=True)
break
elif method == 'session/working-directory/get':
result = {'cwd': directories.get(params['sessionId'], origin)}
elif method == 'session/working-directory/set':
assert not os.path.isabs(params['path']), 'client must preserve relative paths'
previous = directories.get(params['sessionId'], origin)
current = os.path.normpath(os.path.join(previous, params['path']))
directories[params['sessionId']] = current
result = {'cwd': 7 if params['path'] == 'malformed' else current}
else:
raise AssertionError(method)
print(json.dumps({'jsonrpc': '2.0', 'id': frame['id'], 'result': result}), flush=True)
""".strip()
)
with DeepSeekHarness(cwd=str(tmp_path), _launch_args=(sys.executable, str(runtime))) as harness:
first = harness.start_session("first")
second = harness.start_session("second")
assert first.get_working_directory() == str(tmp_path)
assert first.set_working_directory("child") == str(tmp_path / "child")
assert first.get_working_directory() == str(tmp_path / "child")
assert second.get_working_directory() == str(tmp_path)
with pytest.raises(ValidationError):
second.set_working_directory("malformed")