agentengine-sdk-python 0.6.9__py3-none-any.whl → 0.7.0__py3-none-any.whl

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.
Files changed (149) hide show
  1. {agentengine_sdk_python-0.6.9.dist-info → agentengine_sdk_python-0.7.0.dist-info}/METADATA +6 -4
  2. {agentengine_sdk_python-0.6.9.dist-info → agentengine_sdk_python-0.7.0.dist-info}/RECORD +123 -128
  3. ksadk/api/client.py +19 -0
  4. ksadk/cli/cmd_create.py +34 -7
  5. ksadk/cli/cmd_hermes.py +6 -2
  6. ksadk/cli/cmd_invoke.py +208 -41
  7. ksadk/cli/cmd_mcp.py +6 -1
  8. ksadk/cli/cmd_openclaw.py +6 -2
  9. ksadk/cli/cmd_run.py +13 -6
  10. ksadk/cli/network_options.py +5 -3
  11. ksadk/configs/env_registry.py +1 -0
  12. ksadk/conversations/message_projection.py +269 -0
  13. ksadk/conversations/model_context.py +11 -1
  14. ksadk/conversations/runtime.py +81 -13
  15. ksadk/deployment/base.py +1 -1
  16. ksadk/deployment/providers/serverless.py +18 -8
  17. ksadk/memory/adk/resilient_session_service.py +268 -0
  18. ksadk/memory/adk/short_term_memory.py +21 -6
  19. ksadk/runners/adk_runner.py +974 -115
  20. ksadk/runners/langchain_runner.py +219 -7
  21. ksadk/runners/langgraph_runner.py +145 -12
  22. ksadk/runners/remote_runner.py +143 -18
  23. ksadk/server/app.py +148 -6
  24. ksadk/server/static/assets/{ArtifactsPanel-C0meuOdi.js → ArtifactsPanel-D-SSn2ee.js} +1 -1
  25. ksadk/server/static/assets/{CodeBlock-BbI4HPtJ.js → CodeBlock-P16VOLYi.js} +1 -1
  26. ksadk/server/static/assets/{MathMessageMarkdown-DMLQYoLp.js → MathMessageMarkdown-CdGiQWHR.js} +1 -1
  27. ksadk/server/static/assets/{MermaidBlock-DCR8WZ17.js → MermaidBlock-IOcal8cm.js} +6 -6
  28. ksadk/server/static/assets/{NativeTerminalPanel-CKUvWhUz.js → NativeTerminalPanel-DGSDWsei.js} +1 -1
  29. ksadk/server/static/assets/{arc-bj2rZnP5.js → arc-BA13sHc9.js} +1 -1
  30. ksadk/server/static/assets/architecture-7EHR7CIX-BvUlsA6l.js +1 -0
  31. ksadk/server/static/assets/{architectureDiagram-3BPJPVTR-2u1ltDIj.js → architectureDiagram-3BPJPVTR-DzJzgVoL.js} +1 -1
  32. ksadk/server/static/assets/{blockDiagram-GPEHLZMM-D6fvdK20.js → blockDiagram-GPEHLZMM-UTdVcZhn.js} +1 -1
  33. ksadk/server/static/assets/{c4Diagram-AAUBKEIU-BK82L5n5.js → c4Diagram-AAUBKEIU-Dix0GD4m.js} +1 -1
  34. ksadk/server/static/assets/channel-CTuydARX.js +1 -0
  35. ksadk/server/static/assets/{chunk-2J33WTMH-DLVdkeyQ.js → chunk-2J33WTMH-C2JxuZ42.js} +1 -1
  36. ksadk/server/static/assets/{chunk-4BX2VUAB-BbwsYcbW.js → chunk-4BX2VUAB-Be57QPgD.js} +1 -1
  37. ksadk/server/static/assets/{chunk-55IACEB6-DOairxzw.js → chunk-55IACEB6-WjSuy4nv.js} +1 -1
  38. ksadk/server/static/assets/{chunk-727SXJPM-hCO487gs.js → chunk-727SXJPM-BOovP-b5.js} +1 -1
  39. ksadk/server/static/assets/{chunk-AQP2D5EJ-XU8vDOuG.js → chunk-AQP2D5EJ-DOmZNmWj.js} +1 -1
  40. ksadk/server/static/assets/{chunk-FMBD7UC4-PrapyfMu.js → chunk-FMBD7UC4-DzwlodkU.js} +1 -1
  41. ksadk/server/static/assets/{chunk-ND2GUHAM-CboKhTZ3.js → chunk-ND2GUHAM-DMqAikEU.js} +1 -1
  42. ksadk/server/static/assets/chunk-QZHKN3VN-BnCydivS.js +1 -0
  43. ksadk/server/static/assets/classDiagram-4FO5ZUOK-BNHg45Be.js +1 -0
  44. ksadk/server/static/assets/classDiagram-v2-Q7XG4LA2-BNHg45Be.js +1 -0
  45. ksadk/server/static/assets/{cose-bilkent-S5V4N54A-_N8bJ-EU.js → cose-bilkent-S5V4N54A-Zhy2GBnP.js} +1 -1
  46. ksadk/server/static/assets/{dagre-BM42HDAG-FoDlo5J2.js → dagre-BM42HDAG-CbKrtIUz.js} +1 -1
  47. ksadk/server/static/assets/{diagram-2AECGRRQ-BBp6ebBw.js → diagram-2AECGRRQ-BRApgQRK.js} +1 -1
  48. ksadk/server/static/assets/{diagram-5GNKFQAL-CqOvG1PR.js → diagram-5GNKFQAL-D0xbnDiK.js} +1 -1
  49. ksadk/server/static/assets/{diagram-KO2AKTUF-O9T3t6ba.js → diagram-KO2AKTUF-CVugVI9L.js} +1 -1
  50. ksadk/server/static/assets/{diagram-LMA3HP47-CGG8ekBO.js → diagram-LMA3HP47-DozUGgee.js} +1 -1
  51. ksadk/server/static/assets/{diagram-OG6HWLK6-4BmH1EEg.js → diagram-OG6HWLK6-ZOYsKlru.js} +1 -1
  52. ksadk/server/static/assets/{dist-BmuTWcm7.js → dist-2hInLS_J.js} +1 -1
  53. ksadk/server/static/assets/{dist-BMKM66B4.js → dist-8EEyGgDx.js} +1 -1
  54. ksadk/server/static/assets/{dist-Cp1_jKwf.js → dist-8_SfK1IP.js} +1 -1
  55. ksadk/server/static/assets/{dist-CZ6C3x8B.js → dist-B0NP_Z0z.js} +1 -1
  56. ksadk/server/static/assets/{dist-DLeM1OCK.js → dist-BIJVDMuB.js} +1 -1
  57. ksadk/server/static/assets/{dist-Cwhk4lwI.js → dist-BmlMPsCc.js} +1 -1
  58. ksadk/server/static/assets/{dist-D99wf6_X.js → dist-BrCcQ47T.js} +1 -1
  59. ksadk/server/static/assets/{dist-CWiTDN_V.js → dist-BssVmjIo.js} +1 -1
  60. ksadk/server/static/assets/{dist-BVzcWa6D.js → dist-C3HIIaN5.js} +1 -1
  61. ksadk/server/static/assets/{dist-DM0rEQPk.js → dist-CPBtqRTW.js} +1 -1
  62. ksadk/server/static/assets/{dist-BkJjNih3.js → dist-CkEKJFtk.js} +1 -1
  63. ksadk/server/static/assets/{dist-B67uF2uK.js → dist-D-JMdWPQ.js} +1 -1
  64. ksadk/server/static/assets/{dist-DI-oVAXw.js → dist-D-xOD048.js} +1 -1
  65. ksadk/server/static/assets/{dist-6kSZvCZh.js → dist-D57wnGWd.js} +1 -1
  66. ksadk/server/static/assets/{dist-Bk1l05Ni.js → dist-DL_NKc_z.js} +1 -1
  67. ksadk/server/static/assets/{dist-DbaH3p5h.js → dist-DMezvBGJ.js} +1 -1
  68. ksadk/server/static/assets/{dist-CiZHB6ZW.js → dist-DmFaq_Iy.js} +1 -1
  69. ksadk/server/static/assets/{dist-DLMjL01k.js → dist-Dp0k7bWI.js} +1 -1
  70. ksadk/server/static/assets/{dist-DCpZrjaK.js → dist-DpKtt4Bo.js} +1 -1
  71. ksadk/server/static/assets/{dist-D5JGcxBx.js → dist-DyOkZmze.js} +1 -1
  72. ksadk/server/static/assets/{dist-C2DKRFVK.js → dist-DyWsb78Q.js} +1 -1
  73. ksadk/server/static/assets/{dist-S4klkK5W.js → dist-gX6GOLKq.js} +1 -1
  74. ksadk/server/static/assets/{erDiagram-TEJ5UH35-oJhaci7S.js → erDiagram-TEJ5UH35-Bk-9bgE5.js} +1 -1
  75. ksadk/server/static/assets/eventmodeling-FCH6USID-DsAgWnlf.js +1 -0
  76. ksadk/server/static/assets/{flowDiagram-I6XJVG4X-DtTmUan-.js → flowDiagram-I6XJVG4X-Dn9kZVuZ.js} +1 -1
  77. ksadk/server/static/assets/{ganttDiagram-6RSMTGT7-BSsb_eij.js → ganttDiagram-6RSMTGT7-BGxXlJqm.js} +1 -1
  78. ksadk/server/static/assets/gitGraph-WXDBUCRP-BIao9Yjg.js +1 -0
  79. ksadk/server/static/assets/{gitGraphDiagram-PVQCEYII-BR5s6Pws.js → gitGraphDiagram-PVQCEYII-C7Q0X-wi.js} +1 -1
  80. ksadk/server/static/assets/{index-BH7-pC0B.js → index-C00Lwg3L.js} +6 -6
  81. ksadk/server/static/assets/info-J43DQDTF-CyipqGxU.js +1 -0
  82. ksadk/server/static/assets/{infoDiagram-5YYISTIA-DYPc5cvh.js → infoDiagram-5YYISTIA-BO_CSqq5.js} +1 -1
  83. ksadk/server/static/assets/{ishikawaDiagram-YF4QCWOH-MoBdALDg.js → ishikawaDiagram-YF4QCWOH-eYLPLd46.js} +1 -1
  84. ksadk/server/static/assets/{journeyDiagram-JHISSGLW-DgLiQT8l.js → journeyDiagram-JHISSGLW-De9geSb0.js} +1 -1
  85. ksadk/server/static/assets/{kanban-definition-UN3LZRKU-CsFx112F.js → kanban-definition-UN3LZRKU-Cs80kQto.js} +1 -1
  86. ksadk/server/static/assets/{katex-DRsiwpM2.js → katex-DSrCFD8n.js} +1 -1
  87. ksadk/server/static/assets/{linear-TNRQ5Orv.js → linear-CN9sbKTF.js} +1 -1
  88. ksadk/server/static/assets/{mermaid-parser.core-D2eMVOgo.js → mermaid-parser.core-DlgSBU8l.js} +2 -2
  89. ksadk/server/static/assets/{mindmap-definition-RKZ34NQL-BtByYFhL.js → mindmap-definition-RKZ34NQL-9afz7Oug.js} +1 -1
  90. ksadk/server/static/assets/packet-YPE3B663-CGkyRxlg.js +1 -0
  91. ksadk/server/static/assets/pie-LRSECV5Y-M0uWAf4n.js +1 -0
  92. ksadk/server/static/assets/{pieDiagram-4H26LBE5-DPnuCB8c.js → pieDiagram-4H26LBE5-DPv_VstN.js} +1 -1
  93. ksadk/server/static/assets/{quadrantDiagram-W4KKPZXB-miFpCPv4.js → quadrantDiagram-W4KKPZXB-o77wZ1XK.js} +1 -1
  94. ksadk/server/static/assets/radar-GUYGQ44K-Bdbm24-x.js +1 -0
  95. ksadk/server/static/assets/{requirementDiagram-4Y6WPE33-mjB_yYkW.js → requirementDiagram-4Y6WPE33-BYVw81VC.js} +1 -1
  96. ksadk/server/static/assets/{sankeyDiagram-5OEKKPKP-D7hSlZZZ.js → sankeyDiagram-5OEKKPKP-B7nZFMKT.js} +1 -1
  97. ksadk/server/static/assets/{sequenceDiagram-3UESZ5HK-D9Cx6hC3.js → sequenceDiagram-3UESZ5HK-BGRRrFw5.js} +1 -1
  98. ksadk/server/static/assets/{stateDiagram-AJRCARHV-D_zrg47o.js → stateDiagram-AJRCARHV-BUfHCoPs.js} +1 -1
  99. ksadk/server/static/assets/stateDiagram-v2-BHNVJYJU-8xBN_gLM.js +1 -0
  100. ksadk/server/static/assets/{timeline-definition-PNZ67QCA-CwSec0Wa.js → timeline-definition-PNZ67QCA-FbCTGrf9.js} +1 -1
  101. ksadk/server/static/assets/treeView-BLDUP644-npMo0ak1.js +1 -0
  102. ksadk/server/static/assets/treemap-LRROVOQU-CHdzrrdo.js +1 -0
  103. ksadk/server/static/assets/{vennDiagram-CIIHVFJN-agCVobiF.js → vennDiagram-CIIHVFJN-DWgwtPsJ.js} +1 -1
  104. ksadk/server/static/assets/wardley-L42UT6IY-BEQz7wyy.js +1 -0
  105. ksadk/server/static/assets/{wardleyDiagram-YWT4CUSO-TmrlhWNt.js → wardleyDiagram-YWT4CUSO-2x8G-tTM.js} +1 -1
  106. ksadk/server/static/assets/{xychartDiagram-2RQKCTM6-DV71V-ah.js → xychartDiagram-2RQKCTM6-Drma3AYl.js} +1 -1
  107. ksadk/server/static/index.html +1 -1
  108. ksadk/sessions/__init__.py +17 -21
  109. ksadk/sessions/continuity.py +28 -4
  110. ksadk/sessions/postgres_service.py +77 -14
  111. ksadk/sessions/resilience.py +52 -0
  112. ksadk/sessions/resilient.py +360 -0
  113. ksadk/toolsets/__init__.py +2 -0
  114. ksadk/toolsets/workspace.py +49 -10
  115. ksadk/tracing/setup.py +63 -0
  116. ksadk/tui/__init__.py +22 -7
  117. ksadk/tui/loop.py +1877 -0
  118. ksadk/tui/stream_render.py +68 -0
  119. ksadk/version.py +1 -1
  120. ksadk/server/static/assets/architecture-7EHR7CIX-Cr6Xz1_c.js +0 -1
  121. ksadk/server/static/assets/channel-BSSOwFIb.js +0 -1
  122. ksadk/server/static/assets/chunk-QZHKN3VN-XXpdiwUL.js +0 -1
  123. ksadk/server/static/assets/classDiagram-4FO5ZUOK-C5HvGiyK.js +0 -1
  124. ksadk/server/static/assets/classDiagram-v2-Q7XG4LA2-C5HvGiyK.js +0 -1
  125. ksadk/server/static/assets/eventmodeling-FCH6USID-CIgwHJLL.js +0 -1
  126. ksadk/server/static/assets/gitGraph-WXDBUCRP-BVAJ7uo0.js +0 -1
  127. ksadk/server/static/assets/info-J43DQDTF-MT80V4ge.js +0 -1
  128. ksadk/server/static/assets/packet-YPE3B663-BLKhbXZB.js +0 -1
  129. ksadk/server/static/assets/pie-LRSECV5Y-DFOikjSY.js +0 -1
  130. ksadk/server/static/assets/radar-GUYGQ44K-095b1Ikw.js +0 -1
  131. ksadk/server/static/assets/stateDiagram-v2-BHNVJYJU-J9kCb0nC.js +0 -1
  132. ksadk/server/static/assets/treeView-BLDUP644-yinfJEY6.js +0 -1
  133. ksadk/server/static/assets/treemap-LRROVOQU-DosUqN1Q.js +0 -1
  134. ksadk/server/static/assets/wardley-L42UT6IY-Bvp7-5uH.js +0 -1
  135. ksadk/tui/app.py +0 -400
  136. ksadk/tui/clipboard.py +0 -100
  137. ksadk/tui/widgets/__init__.py +0 -22
  138. ksadk/tui/widgets/assistant.py +0 -89
  139. ksadk/tui/widgets/base.py +0 -54
  140. ksadk/tui/widgets/chat_input.py +0 -246
  141. ksadk/tui/widgets/history.py +0 -156
  142. ksadk/tui/widgets/system.py +0 -87
  143. ksadk/tui/widgets/thinking.py +0 -110
  144. ksadk/tui/widgets/tool_call.py +0 -161
  145. ksadk/tui/widgets/user.py +0 -37
  146. {agentengine_sdk_python-0.6.9.dist-info → agentengine_sdk_python-0.7.0.dist-info}/WHEEL +0 -0
  147. {agentengine_sdk_python-0.6.9.dist-info → agentengine_sdk_python-0.7.0.dist-info}/entry_points.txt +0 -0
  148. {agentengine_sdk_python-0.6.9.dist-info → agentengine_sdk_python-0.7.0.dist-info}/licenses/LICENSE +0 -0
  149. {agentengine_sdk_python-0.6.9.dist-info → agentengine_sdk_python-0.7.0.dist-info}/top_level.txt +0 -0
ksadk/cli/cmd_create.py CHANGED
@@ -4,10 +4,13 @@ ksadk create - 创建项目模板
4
4
 
5
5
  import json
6
6
  import platform
7
+ import re
7
8
  import shlex
8
- import click
9
- from pathlib import Path
10
9
  import shutil
10
+
11
+ from pathlib import Path
12
+
13
+ import click
11
14
  import questionary
12
15
  from ksadk.cli.cmd_config import custom_style
13
16
  from ksadk.cli.ui import (
@@ -58,6 +61,32 @@ def _print_quick_start_commands(project_name: str, commands: list[str]) -> None:
58
61
  print_info(line)
59
62
 
60
63
 
64
+ _RUNTIME_AGENT_NAME_MAX_LENGTH = 63
65
+
66
+
67
+ def _normalize_runtime_package_name(project_name: str) -> str:
68
+ """Derive a portable package/runtime name from the project directory name."""
69
+ package_name = re.sub(r"[^A-Za-z0-9_]", "_", project_name.replace("-", "_"))
70
+ if not package_name:
71
+ package_name = "agent"
72
+ if not re.match(r"^[A-Za-z_]", package_name):
73
+ package_name = f"agent_{package_name}"
74
+ if len(package_name) > _RUNTIME_AGENT_NAME_MAX_LENGTH:
75
+ raise ValueError(
76
+ "生成的 Agent 名称 "
77
+ f"'{package_name}' 超过运行时 {_RUNTIME_AGENT_NAME_MAX_LENGTH} 字符限制"
78
+ )
79
+ return package_name
80
+
81
+
82
+ def _runtime_package_name_or_exit(project_name: str) -> str:
83
+ try:
84
+ return _normalize_runtime_package_name(project_name)
85
+ except ValueError as exc:
86
+ print_error(str(exc))
87
+ raise SystemExit(1) from exc
88
+
89
+
61
90
  TEMPLATES = {
62
91
  "adk": {
63
92
  "agent.py": '''"""
@@ -1062,10 +1091,8 @@ def _write_deepagents_service_adapter(
1062
1091
 
1063
1092
  def _wrap_agent_file(from_agent_path: Path, project_name: str, framework: str, agent_var: str):
1064
1093
  """包装单个 Agent 文件到新项目"""
1065
- import re
1066
-
1067
1094
  project_path = Path(project_name)
1068
- package_name = project_path.name.replace('-', '_')
1095
+ package_name = _runtime_package_name_or_exit(project_path.name)
1069
1096
 
1070
1097
  if project_path.exists():
1071
1098
  print_error(f"目录 '{project_name}' 已存在")
@@ -1200,7 +1227,7 @@ def _wrap_agent_directory(from_agent_dir: Path, project_name: str, framework: st
1200
1227
  import shutil
1201
1228
 
1202
1229
  project_path = Path(project_name)
1203
- package_name = project_path.name.replace('-', '_')
1230
+ package_name = _runtime_package_name_or_exit(project_path.name)
1204
1231
  source_dir_name = from_agent_dir.name
1205
1232
 
1206
1233
  if project_path.exists():
@@ -1967,7 +1994,7 @@ def create(project_name: str, framework: str, from_agent_path: str):
1967
1994
  print_kv("创建项目", project_name)
1968
1995
  print_kv("框架", framework)
1969
1996
 
1970
- package_name = project_path.name.replace('-', '_')
1997
+ package_name = _runtime_package_name_or_exit(project_path.name)
1971
1998
  project_path.mkdir(parents=True)
1972
1999
  if framework not in {"openclaw", "hermes"}:
1973
2000
  (project_path / package_name).mkdir(parents=True)
ksadk/cli/cmd_hermes.py CHANGED
@@ -670,8 +670,12 @@ async def _deploy_hermes(
670
670
  region=region,
671
671
  dry_run=dry_run,
672
672
  )
673
- if network_payload:
674
- payload["network"] = network_payload
673
+ # create 默认开公网(network_payload 未显式 enable_public_access 时补 True);update 分支用原始 network_payload(None=保留现有配置)
674
+ create_network_payload = dict(network_payload) if network_payload is not None else {}
675
+ if "enable_public_access" not in create_network_payload:
676
+ create_network_payload["enable_public_access"] = True
677
+ if create_network_payload:
678
+ payload["network"] = create_network_payload
675
679
 
676
680
  print_title("Hermes 云端部署", f"region: {region}")
677
681
  print_kv("名称", agent_name)
ksadk/cli/cmd_invoke.py CHANGED
@@ -84,6 +84,20 @@ except ImportError:
84
84
  )
85
85
  @click.option("--model", help="指定模型名称")
86
86
  @click.option("--show-thinking", is_flag=True, help="显示模型思考过程")
87
+ @click.option(
88
+ "--api-format",
89
+ "api_format",
90
+ type=click.Choice(["auto", "responses", "chat_completions"], case_sensitive=False),
91
+ default="auto",
92
+ show_default=True,
93
+ help="HTTP 协议: auto(优先 responses,不通回退 chat), responses, chat_completions",
94
+ )
95
+ @click.option(
96
+ "--no-alt-screen",
97
+ "no_alt_screen",
98
+ is_flag=True,
99
+ help="兼容参数;TUI 已默认使用 inline viewport 并保留终端 scrollback",
100
+ )
87
101
  def invoke(
88
102
  agent_ref: str,
89
103
  agent_option: str,
@@ -101,6 +115,8 @@ def invoke(
101
115
  verbose_workspace_sync: bool,
102
116
  model: str,
103
117
  show_thinking: bool,
118
+ api_format: str,
119
+ no_alt_screen: bool,
104
120
  ):
105
121
  """与 Agent 进行交互 (本地或远程)。"""
106
122
  run_invoke_command(
@@ -121,6 +137,8 @@ def invoke(
121
137
  model=model,
122
138
  show_thinking=show_thinking,
123
139
  compatibility_alias=True,
140
+ api_format=None if (api_format or "auto").lower() == "auto" else api_format,
141
+ no_alt_screen=no_alt_screen,
124
142
  )
125
143
 
126
144
 
@@ -432,6 +450,8 @@ def run_invoke_command(
432
450
  show_thinking: bool,
433
451
  openclaw_gateway_token: str | None = None,
434
452
  compatibility_alias: bool = False,
453
+ api_format: str | None = None,
454
+ no_alt_screen: bool = False,
435
455
  ):
436
456
  """与 Agent 进行交互 (本地或远程)。"""
437
457
  if compatibility_alias:
@@ -458,7 +478,6 @@ def run_invoke_command(
458
478
  # 加载本地状态
459
479
  state = _load_state()
460
480
  latest_access: dict[str, Any] = {}
461
- reuse_state_session = True
462
481
  target_agent: str | None = None
463
482
 
464
483
  normalized_transport = (transport or "auto").strip().lower() or "auto"
@@ -493,7 +512,6 @@ def run_invoke_command(
493
512
  state=state,
494
513
  persist=_state_matches_target(state, target_agent),
495
514
  )
496
- reuse_state_session = not agent_input or _state_matches_target(state, target_agent)
497
515
 
498
516
  # 优先使用 state 里的 endpoint (如果是对应的 agent)
499
517
  if latest_access.get("endpoint"):
@@ -507,11 +525,11 @@ def run_invoke_command(
507
525
 
508
526
  # API Key
509
527
  api_key = api_key or latest_access.get("api_key") or state.get("api_key")
510
- session_id = (
511
- session
512
- or (state.get("session_id") if reuse_state_session else None)
513
- or str(uuid.uuid4())[:8]
514
- )
528
+ # 单次 --message 调用默认开新 session,避免复用 .agentengine.state 里长上下文
529
+ # 默认每次调用开新 session(单次 -m 与交互 TUI 都是),不复用 .agentengine.state
530
+ # 里的上次 session,避免长上下文累积导致首包变慢。TUI 内多轮续聊由
531
+ # InteractionLoop.session_id 维持;跨次想续聊上次会话用显式 --session 指定。
532
+ session_id = session or str(uuid.uuid4())[:8]
515
533
 
516
534
  next_state = _load_state() or dict(state)
517
535
  for key in ("agent_id", "name", "endpoint", "api_key", "framework"):
@@ -549,7 +567,7 @@ def run_invoke_command(
549
567
 
550
568
  if message:
551
569
  # 单次调用模式
552
- api_format = asyncio.run(
570
+ api_format_resolved = asyncio.run(
553
571
  _resolve_remote_api_format(
554
572
  endpoint=endpoint,
555
573
  api_key=api_key,
@@ -557,9 +575,21 @@ def run_invoke_command(
557
575
  insecure=insecure,
558
576
  state=next_state,
559
577
  latest_access=latest_access,
578
+ api_format=api_format,
579
+ )
580
+ )
581
+ asyncio.run(
582
+ _invoke_once(
583
+ endpoint,
584
+ message,
585
+ runtime_api_key,
586
+ session_id,
587
+ True,
588
+ insecure,
589
+ model,
590
+ api_format_resolved,
560
591
  )
561
592
  )
562
- asyncio.run(_invoke_once(endpoint, message, runtime_api_key, session_id, True, insecure, model, api_format))
563
593
  else:
564
594
  is_hermes_target = _is_hermes_target(next_state, latest_access)
565
595
  is_openclaw_target = _is_openclaw_target(next_state, latest_access)
@@ -634,7 +664,7 @@ def run_invoke_command(
634
664
  insecure=insecure,
635
665
  )
636
666
  else:
637
- api_format = asyncio.run(
667
+ api_format_resolved = asyncio.run(
638
668
  _resolve_remote_api_format(
639
669
  endpoint=endpoint,
640
670
  api_key=api_key,
@@ -642,8 +672,16 @@ def run_invoke_command(
642
672
  insecure=insecure,
643
673
  state=next_state,
644
674
  latest_access=latest_access,
675
+ api_format=api_format,
645
676
  )
646
677
  )
678
+ tui_agent_id = (
679
+ latest_access.get("agent_id")
680
+ or next_state.get("agent_id")
681
+ or target_agent
682
+ or latest_access.get("name")
683
+ or next_state.get("name")
684
+ )
647
685
  _invoke_tui(
648
686
  endpoint,
649
687
  runtime_api_key,
@@ -651,10 +689,13 @@ def run_invoke_command(
651
689
  insecure,
652
690
  model,
653
691
  show_thinking,
654
- api_format=api_format,
692
+ api_format=api_format_resolved,
655
693
  responses_session_header=(
656
694
  "x-openclaw-session-key" if _is_openclaw_target(next_state, latest_access) else None
657
695
  ),
696
+ no_alt_screen=no_alt_screen,
697
+ region=region,
698
+ agent_id=tui_agent_id,
658
699
  )
659
700
 
660
701
 
@@ -669,10 +710,13 @@ def _invoke_tui(
669
710
  show_thinking: bool = False,
670
711
  api_format: str = "chat_completions",
671
712
  responses_session_header: str | None = None,
713
+ no_alt_screen: bool = False,
714
+ region: str = "cn-beijing-6",
715
+ agent_id: str | None = None,
672
716
  ):
673
717
  """使用 TUI 模式调用"""
674
718
  from ksadk.runners.remote_runner import RemoteRunner
675
- from ksadk.tui import AgentTUI
719
+ from ksadk.tui.loop import run_tui
676
720
 
677
721
  runner = RemoteRunner(
678
722
  endpoint=endpoint,
@@ -683,13 +727,90 @@ def _invoke_tui(
683
727
  api_format=api_format,
684
728
  responses_session_header=responses_session_header,
685
729
  )
730
+ # 优先用 ListAgentModels(控制面 action,server 侧封装 runtime catalog,
731
+ # 避开 CLI 直连 runtime /v1/models 对 openclaw/hermes 的鉴权坑)拿模型列表 +
732
+ # 当前模型 metadata。失败 fallback 到 _fetch_tui_model_metadata(直连 /v1/models)。
733
+ model_list = asyncio.run(_fetch_tui_model_list(region=region, agent_id=agent_id))
734
+ if model_list:
735
+ runner.available_models = model_list.get("models") or []
736
+ current_id = model_list.get("current")
737
+ matched = None
738
+ if current_id:
739
+ matched = next((m for m in runner.available_models if m.get("id") == current_id), None)
740
+ if matched is None and runner.available_models:
741
+ matched = runner.available_models[0]
742
+ if matched:
743
+ runner.model_metadata = matched
744
+ # 用户未指定 --model 时,用 ListAgentModels 的 current 作为 runner.model,
745
+ # 让 TUI 启动即显示真实模型名(而非 unknown)。
746
+ if not runner.model and matched.get("id"):
747
+ runner.model = str(matched["id"])
748
+ else:
749
+ # fallback:直连 runtime /v1/models(旧路径,托管 runtime 常失败但不阻断)
750
+ model_metadata = asyncio.run(
751
+ _fetch_tui_model_metadata(endpoint=endpoint, api_key=api_key, model=model)
752
+ )
753
+ if model_metadata:
754
+ runner.model_metadata = model_metadata
686
755
 
687
- app = AgentTUI(
688
- runner=runner,
689
- show_thinking=show_thinking,
690
- project_dir=".",
691
- )
692
- app.run()
756
+ run_tui(runner, show_thinking=show_thinking, project_dir=".", no_alt_screen=no_alt_screen)
757
+
758
+
759
+ async def _fetch_tui_model_list(
760
+ *,
761
+ region: str,
762
+ agent_id: str | None,
763
+ ) -> dict[str, Any] | None:
764
+ """通过控制面 ListAgentModels action 拿 Agent 可选模型列表(对标 hosted UI)。
765
+
766
+ 需 KSYUN access/secret key(env),与 _fetch_remote_access 同套鉴权。
767
+ 失败返回 None(不阻断 TUI,由 _fetch_tui_model_metadata fallback)。
768
+ """
769
+ if not agent_id:
770
+ return None
771
+ try:
772
+ from ksadk.api.client import AgentEngineClient
773
+
774
+ async with AgentEngineClient(region=region) as client:
775
+ return await client.list_agent_models(agent_id=agent_id)
776
+ except Exception:
777
+ return None
778
+
779
+
780
+ async def _fetch_tui_model_metadata(
781
+ *,
782
+ endpoint: str,
783
+ api_key: str | None,
784
+ model: str | None,
785
+ ) -> dict[str, Any] | None:
786
+ try:
787
+ from ksadk.cli.model_catalog import (
788
+ fetch_provider_model_catalog,
789
+ find_model_in_catalog,
790
+ )
791
+ from ksadk.conversations.model_context import normalize_model_metadata
792
+
793
+ catalog = await fetch_provider_model_catalog(
794
+ api_base=endpoint,
795
+ api_key=api_key,
796
+ timeout=3.0,
797
+ )
798
+ except Exception:
799
+ return None
800
+
801
+ if not catalog:
802
+ return None
803
+ raw_models = [item.get("_provider_raw_model") or item for item in catalog]
804
+ matched = find_model_in_catalog(raw_models, model)
805
+ if matched is not None:
806
+ return normalize_model_metadata(matched)
807
+ if model:
808
+ return None
809
+ if len(catalog) == 1:
810
+ item = dict(catalog[0])
811
+ item.pop("_provider_raw_model", None)
812
+ return item
813
+ return None
693
814
 
694
815
 
695
816
  def _load_state() -> dict:
@@ -857,15 +978,14 @@ def _select_runtime_api_key(
857
978
 
858
979
 
859
980
  def _select_remote_api_format(state: dict, latest_access: dict) -> str:
860
- framework = str(
861
- latest_access.get("framework")
862
- or state.get("framework")
863
- or state.get("type")
864
- or ""
865
- ).strip().lower()
866
- if framework in {"openclaw", "hermes"}:
867
- return "responses"
868
- return "chat_completions"
981
+ """auto 模式下所有框架首选 responses(probe 不通再回退 chat_completions)。"""
982
+ return "responses"
983
+
984
+
985
+ # probe 结果进程内短缓存,key=endpoint,value=(过期时间戳, 是否暴露 responses)。
986
+ # 避免每次 invoke 都多打一跳 GET /v1/responses。
987
+ _RESPONSES_ROUTE_CACHE: dict[tuple, tuple[float, bool]] = {}
988
+ _RESPONSES_ROUTE_CACHE_TTL = 30.0
869
989
 
870
990
 
871
991
  async def _resolve_remote_api_format(
@@ -876,23 +996,65 @@ async def _resolve_remote_api_format(
876
996
  insecure: bool,
877
997
  state: dict,
878
998
  latest_access: dict,
999
+ api_format: str | None = None,
879
1000
  ) -> str:
880
- api_format = _select_remote_api_format(state, latest_access)
881
- if api_format != "responses" or not _is_openclaw_target(state, latest_access):
882
- return api_format
883
-
1001
+ # 显式指定 api_format(非 auto)时直接用,不 probe。
1002
+ explicit = str(api_format or "").strip().lower()
1003
+ if explicit in {"responses", "chat_completions"}:
1004
+ return explicit
1005
+
1006
+ # auto:按框架决定首选格式,probe /v1/responses 不通回退 chat_completions。
1007
+ preferred = _select_remote_api_format(state, latest_access)
1008
+ if preferred != "responses":
1009
+ return preferred
884
1010
  probe_api_key = runtime_api_key if runtime_api_key is not None else api_key
885
- if await _probe_openclaw_responses_route(endpoint=endpoint, api_key=probe_api_key, insecure=insecure):
886
- return api_format
1011
+ # openclaw/hermes 的 responses 用独立 gateway 鉴权头,GET 401 不代表 POST 401,
1012
+ # 保留 401=可用的既有行为;普通框架 401=鉴权不通,回退 chat。
1013
+ treat_401 = _is_openclaw_target(state, latest_access) or _is_hermes_target(state, latest_access)
1014
+ if await _probe_responses_route_cached(
1015
+ endpoint=endpoint,
1016
+ api_key=probe_api_key,
1017
+ insecure=insecure,
1018
+ treat_401_as_available=treat_401,
1019
+ ):
1020
+ return "responses"
1021
+ return "chat_completions"
1022
+
887
1023
 
888
- raise click.ClickException(
889
- "当前 OpenClaw endpoint 未暴露 /v1/responses,不能使用 agentengine invoke 的 HTTP TUI。\n"
890
- "可先使用: agentengine dashboard open\n"
891
- "如果要命令行交互,请升级/修复 OpenClaw 镜像或 bootstrap,使 Gateway 暴露 OpenResponses API。"
1024
+ # probe 结果进程内短缓存。cache key 含 treat_401:同一 endpoint 在 openclaw/hermes
1025
+ # 与普通框架下对 401 的解读不同,分开缓存避免串味。
1026
+ async def _probe_responses_route_cached(
1027
+ *,
1028
+ endpoint: str,
1029
+ api_key: str | None,
1030
+ insecure: bool,
1031
+ treat_401_as_available: bool = False,
1032
+ ) -> bool:
1033
+ import time as _time
1034
+
1035
+ cache_key = (endpoint.rstrip("/"), treat_401_as_available)
1036
+ now = _time.time()
1037
+ cached = _RESPONSES_ROUTE_CACHE.get(cache_key)
1038
+ if cached and cached[0] > now:
1039
+ return cached[1]
1040
+
1041
+ available = await _probe_responses_route(
1042
+ endpoint=endpoint,
1043
+ api_key=api_key,
1044
+ insecure=insecure,
1045
+ treat_401_as_available=treat_401_as_available,
892
1046
  )
1047
+ _RESPONSES_ROUTE_CACHE[cache_key] = (now + _RESPONSES_ROUTE_CACHE_TTL, available)
1048
+ return available
893
1049
 
894
1050
 
895
- async def _probe_openclaw_responses_route(*, endpoint: str, api_key: str | None, insecure: bool) -> bool:
1051
+ async def _probe_responses_route(
1052
+ *,
1053
+ endpoint: str,
1054
+ api_key: str | None,
1055
+ insecure: bool,
1056
+ treat_401_as_available: bool = False,
1057
+ ) -> bool:
896
1058
  try:
897
1059
  import httpx
898
1060
  except ImportError:
@@ -912,9 +1074,14 @@ async def _probe_openclaw_responses_route(*, endpoint: str, api_key: str | None,
912
1074
  except Exception:
913
1075
  return False
914
1076
 
915
- # POST-only API routes usually answer GET with 405/422/400. A 404 or HTML 200
916
- # means the request fell through to the OpenClaw UI/router instead of the API.
917
- return response.status_code in {400, 401, 405, 422}
1077
+ # POST-only API routes usually answer GET with 405/422/400 = 路由存在且鉴权通过。
1078
+ # 401 = 未鉴权:带 key 仍 401 说明该 key 不被 responses 路由接受,走 responses 也会 401,
1079
+ # 应回退 chat。openclaw/hermes 的 responses 用独立 gateway 鉴权头,GET 401 不代表 POST 401,
1080
+ # 由 treat_401_as_available 保留其既有行为。404/HTML 200 = 路由不存在。
1081
+ available = response.status_code in {400, 405, 422}
1082
+ if treat_401_as_available and response.status_code == 401:
1083
+ available = True
1084
+ return available
918
1085
 
919
1086
 
920
1087
  def _should_use_hermes_native_tui(*, transport: str, local: bool, state: dict, latest_access: dict) -> bool:
ksadk/cli/cmd_mcp.py CHANGED
@@ -869,7 +869,12 @@ async def _deploy_mcp_async(
869
869
  res = await client.update_mcp(existing_mcp_id, request_data)
870
870
  mcp_id = existing_mcp_id
871
871
  else:
872
- res = await client.create_mcp(request_data)
872
+ # create 默认开公网(network 未显式 enable_public_access 时补 True);update 分支用原始 request_data(network 缺省=保留服务端现有配置)
873
+ create_network = dict(request_data.get("network") or {})
874
+ if "enable_public_access" not in create_network:
875
+ create_network["enable_public_access"] = True
876
+ create_request = {**request_data, "network": create_network}
877
+ res = await client.create_mcp(create_request)
873
878
  if not res:
874
879
  raise remote_error("Server 返回空响应,请检查 MCP 名称是否冲突或服务端日志。")
875
880
  mcp_id = res.get("mcp_id")
ksadk/cli/cmd_openclaw.py CHANGED
@@ -3456,8 +3456,12 @@ async def _deploy_openclaw(
3456
3456
  region=region,
3457
3457
  dry_run=dry_run,
3458
3458
  )
3459
- if network_payload:
3460
- request_data["network"] = network_payload
3459
+ # create 默认开公网(network_payload 未显式 enable_public_access 时补 True);update 分支用原始 network_payload(None=保留现有配置)
3460
+ create_network_payload = dict(network_payload) if network_payload is not None else {}
3461
+ if "enable_public_access" not in create_network_payload:
3462
+ create_network_payload["enable_public_access"] = True
3463
+ if create_network_payload:
3464
+ request_data["network"] = create_network_payload
3461
3465
 
3462
3466
  # 镜像凭证:按目标镜像地址判断仓库类型,避免企业版/第三方误用 KSYUN_ACCOUNT_ID。
3463
3467
  image_credential = None
ksadk/cli/cmd_run.py CHANGED
@@ -32,7 +32,13 @@ from ksadk.cli.ui import (
32
32
  @click.option("--model", help="指定模型名称 (覆盖 .env 配置)")
33
33
  @click.option("--show-thinking", is_flag=True, help="显示模型思考过程")
34
34
  @click.option("--no-stream", is_flag=True, help="禁用流式渲染 (等待完整响应后再渲染)")
35
- def run(agent_dir: str, port: int, interactive: bool, no_trace: bool, model: str, show_thinking: bool, no_stream: bool):
35
+ @click.option(
36
+ "--no-alt-screen",
37
+ "no_alt_screen",
38
+ is_flag=True,
39
+ help="兼容参数;TUI 已默认使用 inline viewport 并保留终端 scrollback",
40
+ )
41
+ def run(agent_dir: str, port: int, interactive: bool, no_trace: bool, model: str, show_thinking: bool, no_stream: bool, no_alt_screen: bool):
36
42
  """运行 Agent (支持 LangChain / LangGraph / DeepAgents / ADK)
37
43
 
38
44
  AGENT_DIR: Agent 项目目录 (默认: 当前目录)
@@ -96,7 +102,7 @@ def run(agent_dir: str, port: int, interactive: bool, no_trace: bool, model: str
96
102
  # 2. 根据框架类型选择处理方式
97
103
  # 所有框架统一使用 _run_custom() 以支持 Langfuse 自动插桩
98
104
  # (Langfuse instrumentation 需要在同一进程内生效)
99
- _run_custom(result, agent_path, port, interactive, no_trace, show_thinking, no_stream)
105
+ _run_custom(result, agent_path, port, interactive, no_trace, show_thinking, no_stream, no_alt_screen)
100
106
 
101
107
 
102
108
  def _run_adk_cli(agent_path: Path, port: int = 8080, command: str = "run"):
@@ -164,6 +170,7 @@ def _run_custom(
164
170
  no_trace: bool,
165
171
  show_thinking: bool,
166
172
  no_stream: bool = False,
173
+ no_alt_screen: bool = False,
167
174
  ):
168
175
  """使用自定义实现 (LangChain/LangGraph/DeepAgents)"""
169
176
  from ksadk.runners.factory import create_runner
@@ -217,13 +224,13 @@ def _run_custom(
217
224
  # 运行
218
225
  if interactive:
219
226
  # TUI 交互模式
220
- from ksadk.tui import AgentTUI
221
- app = AgentTUI(
222
- runner=runner,
227
+ from ksadk.tui.loop import run_tui
228
+ run_tui(
229
+ runner,
223
230
  show_thinking=show_thinking,
224
231
  project_dir=str(agent_path),
232
+ no_alt_screen=no_alt_screen,
225
233
  )
226
- app.run()
227
234
  else:
228
235
  print_success(f"Server running at http://0.0.0.0:{port}")
229
236
  print_kv("API Docs", f"http://0.0.0.0:{port}/docs")
@@ -38,7 +38,7 @@ def network_options(func):
38
38
  click.option(
39
39
  "--enable-public-access/--disable-public-access",
40
40
  default=None,
41
- help="是否开启公网访问;未指定时使用配置文件或平台默认值",
41
+ help="是否开启公网访问;创建时默认开启,更新已有 Agent 时未指定则保留现有配置(显式传入才覆盖)",
42
42
  ),
43
43
  click.option("--enable-vpc-access", is_flag=True, default=False, help="开启 VPC 私网访问"),
44
44
  click.option("--vpc-id", default=None, help="VPC ID(开启 VPC 访问时必填)"),
@@ -92,9 +92,11 @@ def apply_network_config(config: Mapping[str, Any] | None, deploy_target: "Deplo
92
92
  return raw_network[key]
93
93
  return default
94
94
 
95
- deploy_target.network.enable_public_access = bool(
96
- _pick("enable_public_access", "enablePublicAccess", "EnablePublicAccess", default=deploy_target.network.enable_public_access)
95
+ _picked_public_access = _pick(
96
+ "enable_public_access", "enablePublicAccess", "EnablePublicAccess", default=None
97
97
  )
98
+ if _picked_public_access is not None:
99
+ deploy_target.network.enable_public_access = bool(_picked_public_access)
98
100
  deploy_target.network.enable_vpc_access = bool(
99
101
  _pick("enable_vpc_access", "enableVpcAccess", "EnableVpcAccess", default=deploy_target.network.enable_vpc_access)
100
102
  )
@@ -13,6 +13,7 @@ class EnvVarSpec:
13
13
 
14
14
 
15
15
  _ENV_VAR_REGISTRY_ITEMS: tuple[EnvVarSpec, ...] = (
16
+ EnvVarSpec("KSADK_ADK_RESUMABLE", "runners", "Enable ADK invocation resume support.", "false"),
16
17
  EnvVarSpec("KSADK_ADK_SESSION_BACKEND", "sessions", "ADK-native session backend selector."),
17
18
  EnvVarSpec("KSADK_ADK_SESSION_PATH", "sessions", "ADK-native SQLite session database path."),
18
19
  EnvVarSpec("KSADK_ADK_SESSION_URL", "sessions", "ADK-native database session URL.", sensitive=True),