This commit is contained in:
hupenglong1
2026-05-20 15:04:19 +08:00
parent c4b935692c
commit 1a94cec822
27 changed files with 4831 additions and 1693 deletions
+1 -1
View File
@@ -158,7 +158,7 @@ class AgentRuntimeConfig:
cwd: Path
max_turns: int = DEFAULT_MAX_TURNS
command_timeout_seconds: float = 30.0
max_output_chars: int = 12000
max_output_chars: int = 50000 # ≈ 20k token (Chinese/code mix ≈ 2.5 chars/token)
stream_model_responses: bool = False
auto_snip_threshold_tokens: int | None = None
auto_compact_threshold_tokens: int | None = None
+40
View File
@@ -774,6 +774,20 @@ print(f"{'appended' if MODE == 'a' else 'wrote'} {target} ({len(content)} chars)
def resolve_workspace_path(self, raw_path: str) -> str:
value = raw_path.strip() or '.'
# 系统提示里给的"当前会话目录"通常是本地后端的绝对/相对路径,例如
# `.port_sessions/accounts/<acc>/sessions/<sid>/input` 或
# `/home/mi/.../.port_sessions/accounts/<acc>/sessions/<sid>/output/foo.csv`。
# 远端 Jupyter 没这套目录结构。识别这种 pattern 后,把 input/output/
# scratchpad bucket 映射到远端 workspace 的同名目录。
bucket_match = self._match_local_session_bucket(value)
if bucket_match is not None:
bucket, rest = bucket_match
tail = f'/{rest}' if rest else ''
return normalize_posix_path(
f'{self.binding.workspace_cwd}/{bucket}{tail}'
)
if value.startswith('/'):
return normalize_posix_path(value)
path = PurePosixPath(value)
@@ -787,8 +801,34 @@ print(f"{'appended' if MODE == 'a' else 'wrote'} {target} ({len(content)} chars)
if path.parts and path.parts[0] in {'scratchpad', 'scratch'}:
tail = PurePosixPath(*path.parts[1:]) if len(path.parts) > 1 else PurePosixPath()
return normalize_posix_path(f'{self.binding.workspace_cwd}/scratchpad/{tail}')
if path.parts and path.parts[0] in {'input', 'inputs'}:
tail = PurePosixPath(*path.parts[1:]) if len(path.parts) > 1 else PurePosixPath()
return normalize_posix_path(f'{self.binding.workspace_cwd}/input/{tail}')
return normalize_posix_path(f'{self.binding.workspace_cwd}/{value}')
_LOCAL_SESSION_BUCKET_RE = re.compile(
r'(?:^|/)\.port_sessions/accounts/[^/]+/sessions/[^/]+/'
r'(?P<bucket>input|inputs|output|outputs|scratchpad|scratch)'
r'(?:/(?P<rest>.*))?$'
)
@classmethod
def _match_local_session_bucket(cls, raw: str) -> tuple[str, str] | None:
m = cls._LOCAL_SESSION_BUCKET_RE.search(raw)
if not m:
return None
bucket = m.group('bucket')
normalized = {
'input': 'input',
'inputs': 'input',
'output': 'output',
'outputs': 'output',
'scratchpad': 'scratchpad',
'scratch': 'scratchpad',
}[bucket]
rest = m.group('rest') or ''
return normalized, rest
def _remote_env_prefix(self) -> str:
exports = {
'ZK_AGENT_WORKSPACE': self.binding.workspace_cwd,
+23 -21
View File
@@ -15,28 +15,28 @@ from .agent_types import (
OutputSchemaConfig,
UsageStats,
)
@dataclass(frozen=True)
class StoredSession:
session_id: str
messages: tuple[str, ...]
input_tokens: int
output_tokens: int
@dataclass(frozen=True)
class StoredSession:
session_id: str
messages: tuple[str, ...]
input_tokens: int
output_tokens: int
DEFAULT_SESSION_DIR = Path('.port_sessions')
DEFAULT_AGENT_SESSION_DIR = DEFAULT_SESSION_DIR / 'agent'
def save_session(session: StoredSession, directory: Path | None = None) -> Path:
target_dir = directory or DEFAULT_SESSION_DIR
target_dir.mkdir(parents=True, exist_ok=True)
path = target_dir / f'{session.session_id}.json'
path.write_text(json.dumps(asdict(session), indent=2))
return path
def save_session(session: StoredSession, directory: Path | None = None) -> Path:
target_dir = directory or DEFAULT_SESSION_DIR
target_dir.mkdir(parents=True, exist_ok=True)
path = target_dir / f'{session.session_id}.json'
path.write_text(json.dumps(asdict(session), indent=2))
return path
def load_session(session_id: str, directory: Path | None = None) -> StoredSession:
target_dir = directory or DEFAULT_SESSION_DIR
data = json.loads((target_dir / f'{session_id}.json').read_text())
@@ -68,6 +68,7 @@ class StoredAgentSession:
budget_state: JSONDict
plugin_state: JSONDict
scratchpad_directory: str | None = None
is_training: bool = False
def save_agent_session(session: StoredAgentSession, directory: Path | None = None) -> Path:
@@ -118,6 +119,7 @@ def load_agent_session(session_id: str, directory: Path | None = None) -> Stored
if isinstance(data.get('scratchpad_directory'), str)
else None
),
is_training=bool(data.get('is_training', False)),
)
@@ -222,7 +224,7 @@ def deserialize_runtime_config(payload: JSONDict) -> AgentRuntimeConfig:
cwd=Path(str(payload['cwd'])).resolve(),
max_turns=int(payload.get('max_turns', DEFAULT_MAX_TURNS)),
command_timeout_seconds=float(payload.get('command_timeout_seconds', 30.0)),
max_output_chars=int(payload.get('max_output_chars', 12000)),
max_output_chars=int(payload.get('max_output_chars', 50000)),
stream_model_responses=bool(payload.get('stream_model_responses', False)),
auto_snip_threshold_tokens=_optional_int(payload.get('auto_snip_threshold_tokens')),
auto_compact_threshold_tokens=_optional_int(payload.get('auto_compact_threshold_tokens')),