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.
- {agentengine_sdk_python-0.6.9.dist-info → agentengine_sdk_python-0.7.0.dist-info}/METADATA +6 -4
- {agentengine_sdk_python-0.6.9.dist-info → agentengine_sdk_python-0.7.0.dist-info}/RECORD +123 -128
- ksadk/api/client.py +19 -0
- ksadk/cli/cmd_create.py +34 -7
- ksadk/cli/cmd_hermes.py +6 -2
- ksadk/cli/cmd_invoke.py +208 -41
- ksadk/cli/cmd_mcp.py +6 -1
- ksadk/cli/cmd_openclaw.py +6 -2
- ksadk/cli/cmd_run.py +13 -6
- ksadk/cli/network_options.py +5 -3
- ksadk/configs/env_registry.py +1 -0
- ksadk/conversations/message_projection.py +269 -0
- ksadk/conversations/model_context.py +11 -1
- ksadk/conversations/runtime.py +81 -13
- ksadk/deployment/base.py +1 -1
- ksadk/deployment/providers/serverless.py +18 -8
- ksadk/memory/adk/resilient_session_service.py +268 -0
- ksadk/memory/adk/short_term_memory.py +21 -6
- ksadk/runners/adk_runner.py +974 -115
- ksadk/runners/langchain_runner.py +219 -7
- ksadk/runners/langgraph_runner.py +145 -12
- ksadk/runners/remote_runner.py +143 -18
- ksadk/server/app.py +148 -6
- ksadk/server/static/assets/{ArtifactsPanel-C0meuOdi.js → ArtifactsPanel-D-SSn2ee.js} +1 -1
- ksadk/server/static/assets/{CodeBlock-BbI4HPtJ.js → CodeBlock-P16VOLYi.js} +1 -1
- ksadk/server/static/assets/{MathMessageMarkdown-DMLQYoLp.js → MathMessageMarkdown-CdGiQWHR.js} +1 -1
- ksadk/server/static/assets/{MermaidBlock-DCR8WZ17.js → MermaidBlock-IOcal8cm.js} +6 -6
- ksadk/server/static/assets/{NativeTerminalPanel-CKUvWhUz.js → NativeTerminalPanel-DGSDWsei.js} +1 -1
- ksadk/server/static/assets/{arc-bj2rZnP5.js → arc-BA13sHc9.js} +1 -1
- ksadk/server/static/assets/architecture-7EHR7CIX-BvUlsA6l.js +1 -0
- ksadk/server/static/assets/{architectureDiagram-3BPJPVTR-2u1ltDIj.js → architectureDiagram-3BPJPVTR-DzJzgVoL.js} +1 -1
- ksadk/server/static/assets/{blockDiagram-GPEHLZMM-D6fvdK20.js → blockDiagram-GPEHLZMM-UTdVcZhn.js} +1 -1
- ksadk/server/static/assets/{c4Diagram-AAUBKEIU-BK82L5n5.js → c4Diagram-AAUBKEIU-Dix0GD4m.js} +1 -1
- ksadk/server/static/assets/channel-CTuydARX.js +1 -0
- ksadk/server/static/assets/{chunk-2J33WTMH-DLVdkeyQ.js → chunk-2J33WTMH-C2JxuZ42.js} +1 -1
- ksadk/server/static/assets/{chunk-4BX2VUAB-BbwsYcbW.js → chunk-4BX2VUAB-Be57QPgD.js} +1 -1
- ksadk/server/static/assets/{chunk-55IACEB6-DOairxzw.js → chunk-55IACEB6-WjSuy4nv.js} +1 -1
- ksadk/server/static/assets/{chunk-727SXJPM-hCO487gs.js → chunk-727SXJPM-BOovP-b5.js} +1 -1
- ksadk/server/static/assets/{chunk-AQP2D5EJ-XU8vDOuG.js → chunk-AQP2D5EJ-DOmZNmWj.js} +1 -1
- ksadk/server/static/assets/{chunk-FMBD7UC4-PrapyfMu.js → chunk-FMBD7UC4-DzwlodkU.js} +1 -1
- ksadk/server/static/assets/{chunk-ND2GUHAM-CboKhTZ3.js → chunk-ND2GUHAM-DMqAikEU.js} +1 -1
- ksadk/server/static/assets/chunk-QZHKN3VN-BnCydivS.js +1 -0
- ksadk/server/static/assets/classDiagram-4FO5ZUOK-BNHg45Be.js +1 -0
- ksadk/server/static/assets/classDiagram-v2-Q7XG4LA2-BNHg45Be.js +1 -0
- ksadk/server/static/assets/{cose-bilkent-S5V4N54A-_N8bJ-EU.js → cose-bilkent-S5V4N54A-Zhy2GBnP.js} +1 -1
- ksadk/server/static/assets/{dagre-BM42HDAG-FoDlo5J2.js → dagre-BM42HDAG-CbKrtIUz.js} +1 -1
- ksadk/server/static/assets/{diagram-2AECGRRQ-BBp6ebBw.js → diagram-2AECGRRQ-BRApgQRK.js} +1 -1
- ksadk/server/static/assets/{diagram-5GNKFQAL-CqOvG1PR.js → diagram-5GNKFQAL-D0xbnDiK.js} +1 -1
- ksadk/server/static/assets/{diagram-KO2AKTUF-O9T3t6ba.js → diagram-KO2AKTUF-CVugVI9L.js} +1 -1
- ksadk/server/static/assets/{diagram-LMA3HP47-CGG8ekBO.js → diagram-LMA3HP47-DozUGgee.js} +1 -1
- ksadk/server/static/assets/{diagram-OG6HWLK6-4BmH1EEg.js → diagram-OG6HWLK6-ZOYsKlru.js} +1 -1
- ksadk/server/static/assets/{dist-BmuTWcm7.js → dist-2hInLS_J.js} +1 -1
- ksadk/server/static/assets/{dist-BMKM66B4.js → dist-8EEyGgDx.js} +1 -1
- ksadk/server/static/assets/{dist-Cp1_jKwf.js → dist-8_SfK1IP.js} +1 -1
- ksadk/server/static/assets/{dist-CZ6C3x8B.js → dist-B0NP_Z0z.js} +1 -1
- ksadk/server/static/assets/{dist-DLeM1OCK.js → dist-BIJVDMuB.js} +1 -1
- ksadk/server/static/assets/{dist-Cwhk4lwI.js → dist-BmlMPsCc.js} +1 -1
- ksadk/server/static/assets/{dist-D99wf6_X.js → dist-BrCcQ47T.js} +1 -1
- ksadk/server/static/assets/{dist-CWiTDN_V.js → dist-BssVmjIo.js} +1 -1
- ksadk/server/static/assets/{dist-BVzcWa6D.js → dist-C3HIIaN5.js} +1 -1
- ksadk/server/static/assets/{dist-DM0rEQPk.js → dist-CPBtqRTW.js} +1 -1
- ksadk/server/static/assets/{dist-BkJjNih3.js → dist-CkEKJFtk.js} +1 -1
- ksadk/server/static/assets/{dist-B67uF2uK.js → dist-D-JMdWPQ.js} +1 -1
- ksadk/server/static/assets/{dist-DI-oVAXw.js → dist-D-xOD048.js} +1 -1
- ksadk/server/static/assets/{dist-6kSZvCZh.js → dist-D57wnGWd.js} +1 -1
- ksadk/server/static/assets/{dist-Bk1l05Ni.js → dist-DL_NKc_z.js} +1 -1
- ksadk/server/static/assets/{dist-DbaH3p5h.js → dist-DMezvBGJ.js} +1 -1
- ksadk/server/static/assets/{dist-CiZHB6ZW.js → dist-DmFaq_Iy.js} +1 -1
- ksadk/server/static/assets/{dist-DLMjL01k.js → dist-Dp0k7bWI.js} +1 -1
- ksadk/server/static/assets/{dist-DCpZrjaK.js → dist-DpKtt4Bo.js} +1 -1
- ksadk/server/static/assets/{dist-D5JGcxBx.js → dist-DyOkZmze.js} +1 -1
- ksadk/server/static/assets/{dist-C2DKRFVK.js → dist-DyWsb78Q.js} +1 -1
- ksadk/server/static/assets/{dist-S4klkK5W.js → dist-gX6GOLKq.js} +1 -1
- ksadk/server/static/assets/{erDiagram-TEJ5UH35-oJhaci7S.js → erDiagram-TEJ5UH35-Bk-9bgE5.js} +1 -1
- ksadk/server/static/assets/eventmodeling-FCH6USID-DsAgWnlf.js +1 -0
- ksadk/server/static/assets/{flowDiagram-I6XJVG4X-DtTmUan-.js → flowDiagram-I6XJVG4X-Dn9kZVuZ.js} +1 -1
- ksadk/server/static/assets/{ganttDiagram-6RSMTGT7-BSsb_eij.js → ganttDiagram-6RSMTGT7-BGxXlJqm.js} +1 -1
- ksadk/server/static/assets/gitGraph-WXDBUCRP-BIao9Yjg.js +1 -0
- ksadk/server/static/assets/{gitGraphDiagram-PVQCEYII-BR5s6Pws.js → gitGraphDiagram-PVQCEYII-C7Q0X-wi.js} +1 -1
- ksadk/server/static/assets/{index-BH7-pC0B.js → index-C00Lwg3L.js} +6 -6
- ksadk/server/static/assets/info-J43DQDTF-CyipqGxU.js +1 -0
- ksadk/server/static/assets/{infoDiagram-5YYISTIA-DYPc5cvh.js → infoDiagram-5YYISTIA-BO_CSqq5.js} +1 -1
- ksadk/server/static/assets/{ishikawaDiagram-YF4QCWOH-MoBdALDg.js → ishikawaDiagram-YF4QCWOH-eYLPLd46.js} +1 -1
- ksadk/server/static/assets/{journeyDiagram-JHISSGLW-DgLiQT8l.js → journeyDiagram-JHISSGLW-De9geSb0.js} +1 -1
- ksadk/server/static/assets/{kanban-definition-UN3LZRKU-CsFx112F.js → kanban-definition-UN3LZRKU-Cs80kQto.js} +1 -1
- ksadk/server/static/assets/{katex-DRsiwpM2.js → katex-DSrCFD8n.js} +1 -1
- ksadk/server/static/assets/{linear-TNRQ5Orv.js → linear-CN9sbKTF.js} +1 -1
- ksadk/server/static/assets/{mermaid-parser.core-D2eMVOgo.js → mermaid-parser.core-DlgSBU8l.js} +2 -2
- ksadk/server/static/assets/{mindmap-definition-RKZ34NQL-BtByYFhL.js → mindmap-definition-RKZ34NQL-9afz7Oug.js} +1 -1
- ksadk/server/static/assets/packet-YPE3B663-CGkyRxlg.js +1 -0
- ksadk/server/static/assets/pie-LRSECV5Y-M0uWAf4n.js +1 -0
- ksadk/server/static/assets/{pieDiagram-4H26LBE5-DPnuCB8c.js → pieDiagram-4H26LBE5-DPv_VstN.js} +1 -1
- ksadk/server/static/assets/{quadrantDiagram-W4KKPZXB-miFpCPv4.js → quadrantDiagram-W4KKPZXB-o77wZ1XK.js} +1 -1
- ksadk/server/static/assets/radar-GUYGQ44K-Bdbm24-x.js +1 -0
- ksadk/server/static/assets/{requirementDiagram-4Y6WPE33-mjB_yYkW.js → requirementDiagram-4Y6WPE33-BYVw81VC.js} +1 -1
- ksadk/server/static/assets/{sankeyDiagram-5OEKKPKP-D7hSlZZZ.js → sankeyDiagram-5OEKKPKP-B7nZFMKT.js} +1 -1
- ksadk/server/static/assets/{sequenceDiagram-3UESZ5HK-D9Cx6hC3.js → sequenceDiagram-3UESZ5HK-BGRRrFw5.js} +1 -1
- ksadk/server/static/assets/{stateDiagram-AJRCARHV-D_zrg47o.js → stateDiagram-AJRCARHV-BUfHCoPs.js} +1 -1
- ksadk/server/static/assets/stateDiagram-v2-BHNVJYJU-8xBN_gLM.js +1 -0
- ksadk/server/static/assets/{timeline-definition-PNZ67QCA-CwSec0Wa.js → timeline-definition-PNZ67QCA-FbCTGrf9.js} +1 -1
- ksadk/server/static/assets/treeView-BLDUP644-npMo0ak1.js +1 -0
- ksadk/server/static/assets/treemap-LRROVOQU-CHdzrrdo.js +1 -0
- ksadk/server/static/assets/{vennDiagram-CIIHVFJN-agCVobiF.js → vennDiagram-CIIHVFJN-DWgwtPsJ.js} +1 -1
- ksadk/server/static/assets/wardley-L42UT6IY-BEQz7wyy.js +1 -0
- ksadk/server/static/assets/{wardleyDiagram-YWT4CUSO-TmrlhWNt.js → wardleyDiagram-YWT4CUSO-2x8G-tTM.js} +1 -1
- ksadk/server/static/assets/{xychartDiagram-2RQKCTM6-DV71V-ah.js → xychartDiagram-2RQKCTM6-Drma3AYl.js} +1 -1
- ksadk/server/static/index.html +1 -1
- ksadk/sessions/__init__.py +17 -21
- ksadk/sessions/continuity.py +28 -4
- ksadk/sessions/postgres_service.py +77 -14
- ksadk/sessions/resilience.py +52 -0
- ksadk/sessions/resilient.py +360 -0
- ksadk/toolsets/__init__.py +2 -0
- ksadk/toolsets/workspace.py +49 -10
- ksadk/tracing/setup.py +63 -0
- ksadk/tui/__init__.py +22 -7
- ksadk/tui/loop.py +1877 -0
- ksadk/tui/stream_render.py +68 -0
- ksadk/version.py +1 -1
- ksadk/server/static/assets/architecture-7EHR7CIX-Cr6Xz1_c.js +0 -1
- ksadk/server/static/assets/channel-BSSOwFIb.js +0 -1
- ksadk/server/static/assets/chunk-QZHKN3VN-XXpdiwUL.js +0 -1
- ksadk/server/static/assets/classDiagram-4FO5ZUOK-C5HvGiyK.js +0 -1
- ksadk/server/static/assets/classDiagram-v2-Q7XG4LA2-C5HvGiyK.js +0 -1
- ksadk/server/static/assets/eventmodeling-FCH6USID-CIgwHJLL.js +0 -1
- ksadk/server/static/assets/gitGraph-WXDBUCRP-BVAJ7uo0.js +0 -1
- ksadk/server/static/assets/info-J43DQDTF-MT80V4ge.js +0 -1
- ksadk/server/static/assets/packet-YPE3B663-BLKhbXZB.js +0 -1
- ksadk/server/static/assets/pie-LRSECV5Y-DFOikjSY.js +0 -1
- ksadk/server/static/assets/radar-GUYGQ44K-095b1Ikw.js +0 -1
- ksadk/server/static/assets/stateDiagram-v2-BHNVJYJU-J9kCb0nC.js +0 -1
- ksadk/server/static/assets/treeView-BLDUP644-yinfJEY6.js +0 -1
- ksadk/server/static/assets/treemap-LRROVOQU-DosUqN1Q.js +0 -1
- ksadk/server/static/assets/wardley-L42UT6IY-Bvp7-5uH.js +0 -1
- ksadk/tui/app.py +0 -400
- ksadk/tui/clipboard.py +0 -100
- ksadk/tui/widgets/__init__.py +0 -22
- ksadk/tui/widgets/assistant.py +0 -89
- ksadk/tui/widgets/base.py +0 -54
- ksadk/tui/widgets/chat_input.py +0 -246
- ksadk/tui/widgets/history.py +0 -156
- ksadk/tui/widgets/system.py +0 -87
- ksadk/tui/widgets/thinking.py +0 -110
- ksadk/tui/widgets/tool_call.py +0 -161
- ksadk/tui/widgets/user.py +0 -37
- {agentengine_sdk_python-0.6.9.dist-info → agentengine_sdk_python-0.7.0.dist-info}/WHEEL +0 -0
- {agentengine_sdk_python-0.6.9.dist-info → agentengine_sdk_python-0.7.0.dist-info}/entry_points.txt +0 -0
- {agentengine_sdk_python-0.6.9.dist-info → agentengine_sdk_python-0.7.0.dist-info}/licenses/LICENSE +0 -0
- {agentengine_sdk_python-0.6.9.dist-info → agentengine_sdk_python-0.7.0.dist-info}/top_level.txt +0 -0
ksadk/runners/remote_runner.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""
|
|
2
2
|
RemoteRunner - 远程 Agent 运行时
|
|
3
3
|
|
|
4
|
-
与
|
|
4
|
+
与 InteractionLoop 配合使用,提供和本地 Runner 一致的接口
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
7
|
import json
|
|
@@ -43,6 +43,11 @@ class RemoteRunner(BaseRunner):
|
|
|
43
43
|
self._agent = None # 兼容 BaseRunner
|
|
44
44
|
self._responses_tool_names: dict[str, str] = {}
|
|
45
45
|
self._responses_tool_args: dict[str, str] = {}
|
|
46
|
+
self._responses_streamed_item_keys: set[str] = set()
|
|
47
|
+
self._responses_text_streamed = False
|
|
48
|
+
self._responses_reasoning_streamed = False
|
|
49
|
+
self._observed_model: str | None = None # 流式回包里观察到的真实模型名
|
|
50
|
+
self.available_models: list[dict[str, Any]] | None = None # ListAgentModels 拿到的可选模型列表
|
|
46
51
|
|
|
47
52
|
@staticmethod
|
|
48
53
|
def _normalize_api_format(api_format: Optional[str]) -> str:
|
|
@@ -362,6 +367,14 @@ class RemoteRunner(BaseRunner):
|
|
|
362
367
|
"""流式调用远程 Agent"""
|
|
363
368
|
import httpx
|
|
364
369
|
|
|
370
|
+
# 工具 item/call_id 只在单次 response 内有效。RemoteRunner 会被 TUI 多轮复用,
|
|
371
|
+
# 若保留上一轮的追踪状态,runtime 复用 id 时 response.completed 会被误判为重放。
|
|
372
|
+
self._responses_tool_names.clear()
|
|
373
|
+
self._responses_tool_args.clear()
|
|
374
|
+
self._responses_streamed_item_keys.clear()
|
|
375
|
+
self._responses_text_streamed = False
|
|
376
|
+
self._responses_reasoning_streamed = False
|
|
377
|
+
|
|
365
378
|
user_input = input_data.get("input", "")
|
|
366
379
|
session_id = input_data.get("session_id") or self.session_id
|
|
367
380
|
|
|
@@ -406,6 +419,17 @@ class RemoteRunner(BaseRunner):
|
|
|
406
419
|
try:
|
|
407
420
|
data = json.loads(data_str)
|
|
408
421
|
|
|
422
|
+
# 记录回包里的真实模型名:
|
|
423
|
+
# chat completions 顶层 data.model;responses 格式在
|
|
424
|
+
# data.response.model(response.completed 事件的 response 对象)。
|
|
425
|
+
observed = data.get("model") if isinstance(data, Mapping) else None
|
|
426
|
+
if not observed and isinstance(data, Mapping):
|
|
427
|
+
resp = data.get("response")
|
|
428
|
+
if isinstance(resp, Mapping):
|
|
429
|
+
observed = resp.get("model")
|
|
430
|
+
if observed and not isinstance(observed, (list, dict)):
|
|
431
|
+
self._observed_model = str(observed)
|
|
432
|
+
|
|
409
433
|
if self.api_format == "responses":
|
|
410
434
|
async for item in self._iter_responses_stream_events(
|
|
411
435
|
data, event_name=event_name
|
|
@@ -413,11 +437,26 @@ class RemoteRunner(BaseRunner):
|
|
|
413
437
|
yield item
|
|
414
438
|
continue
|
|
415
439
|
|
|
440
|
+
# 兜底:api_format 标成 chat 但 runtime 实发 responses 格式
|
|
441
|
+
# 事件(event_name 或 data.type 以 response. 开头)→ 按 responses
|
|
442
|
+
# 解析,否则 response.reasoning.delta 会被 chat 路径当顶层 text 显示。
|
|
443
|
+
_ev = event_name or (str(data.get("type")) if isinstance(data, Mapping) else "")
|
|
444
|
+
if _ev.startswith("response."):
|
|
445
|
+
async for item in self._iter_responses_stream_events(
|
|
446
|
+
data, event_name=event_name
|
|
447
|
+
):
|
|
448
|
+
yield item
|
|
449
|
+
continue
|
|
450
|
+
|
|
416
451
|
# 解析 OpenAI Chat Completions 流式格式
|
|
417
452
|
choices = data.get("choices", [])
|
|
418
453
|
usage = data.get("usage")
|
|
419
454
|
if isinstance(usage, Mapping):
|
|
420
|
-
|
|
455
|
+
final_chunk = {"type": "final", "usage": dict(usage)}
|
|
456
|
+
metadata = data.get("metadata")
|
|
457
|
+
if isinstance(metadata, Mapping):
|
|
458
|
+
final_chunk["metadata"] = dict(metadata)
|
|
459
|
+
yield final_chunk
|
|
421
460
|
final_sent = True
|
|
422
461
|
if choices:
|
|
423
462
|
delta = choices[0].get("delta", {})
|
|
@@ -429,6 +468,13 @@ class RemoteRunner(BaseRunner):
|
|
|
429
468
|
if content:
|
|
430
469
|
accumulated_text += content
|
|
431
470
|
yield {"delta": content, "type": "text"}
|
|
471
|
+
else:
|
|
472
|
+
# 非标准简化流(runtime 直发顶层 delta,无 choices 包装):
|
|
473
|
+
# 兜底提取顶层 delta 作为正文,与 _extract_content 口径一致。
|
|
474
|
+
top_delta = data.get("delta")
|
|
475
|
+
if isinstance(top_delta, str) and top_delta:
|
|
476
|
+
accumulated_text += top_delta
|
|
477
|
+
yield {"delta": top_delta, "type": "text"}
|
|
432
478
|
|
|
433
479
|
except json.JSONDecodeError:
|
|
434
480
|
pass
|
|
@@ -484,11 +530,14 @@ class RemoteRunner(BaseRunner):
|
|
|
484
530
|
def _remember_responses_tool(
|
|
485
531
|
self, key: str, item: Dict[str, Any], name: str, args: str
|
|
486
532
|
) -> None:
|
|
533
|
+
if key:
|
|
534
|
+
self._responses_streamed_item_keys.add(key)
|
|
487
535
|
if key:
|
|
488
536
|
self._responses_tool_names[key] = name
|
|
489
537
|
self._responses_tool_args[key] = args
|
|
490
538
|
call_id = str(item.get("call_id") or "")
|
|
491
539
|
if call_id:
|
|
540
|
+
self._responses_streamed_item_keys.add(call_id)
|
|
492
541
|
self._responses_tool_names[call_id] = name
|
|
493
542
|
self._responses_tool_args[call_id] = args
|
|
494
543
|
|
|
@@ -526,6 +575,8 @@ class RemoteRunner(BaseRunner):
|
|
|
526
575
|
data: Dict[str, Any],
|
|
527
576
|
*,
|
|
528
577
|
status: str,
|
|
578
|
+
replay_completed_text: bool = False,
|
|
579
|
+
replay_completed_reasoning: bool = False,
|
|
529
580
|
) -> AsyncIterator[Dict[str, Any]]:
|
|
530
581
|
item = data.get("item") or data.get("output_item") or data
|
|
531
582
|
if not isinstance(item, dict):
|
|
@@ -541,15 +592,20 @@ class RemoteRunner(BaseRunner):
|
|
|
541
592
|
else item.get("args", item.get("input"))
|
|
542
593
|
)
|
|
543
594
|
self._remember_responses_tool(key, item, name, args)
|
|
544
|
-
yield {"type": "tool_call", "tool_name": name, "tool_args": args, "status": status}
|
|
595
|
+
yield {"type": "tool_call", "tool_name": name, "tool_args": args, "status": status, "call_id": key}
|
|
545
596
|
return
|
|
546
597
|
|
|
547
598
|
if item_type == "function_call_output":
|
|
548
599
|
name = self._responses_tool_name(key, item)
|
|
600
|
+
if key:
|
|
601
|
+
self._responses_streamed_item_keys.add(key)
|
|
602
|
+
call_id = str(item.get("call_id") or "")
|
|
603
|
+
if call_id:
|
|
604
|
+
self._responses_streamed_item_keys.add(call_id)
|
|
549
605
|
output = self._stringify_responses_payload(
|
|
550
606
|
item.get("output") if "output" in item else item.get("result", item.get("content"))
|
|
551
607
|
)
|
|
552
|
-
yield {"type": "tool_result", "tool_name": name, "tool_output": output}
|
|
608
|
+
yield {"type": "tool_result", "tool_name": name, "tool_output": output, "call_id": call_id or key}
|
|
553
609
|
return
|
|
554
610
|
|
|
555
611
|
if item_type == "mcp_approval_request":
|
|
@@ -576,13 +632,15 @@ class RemoteRunner(BaseRunner):
|
|
|
576
632
|
|
|
577
633
|
if item_type in {"reasoning", "reasoning_summary", "reasoning_summary_text"}:
|
|
578
634
|
text = self._responses_item_text(item)
|
|
579
|
-
if text:
|
|
635
|
+
if text and (status != "completed" or replay_completed_reasoning):
|
|
636
|
+
self._responses_reasoning_streamed = True
|
|
580
637
|
yield {"delta": text, "type": "thinking"}
|
|
581
638
|
return
|
|
582
639
|
|
|
583
640
|
if item_type == "message":
|
|
584
641
|
text = self._responses_item_text(item)
|
|
585
|
-
if text and status != "completed":
|
|
642
|
+
if text and (status != "completed" or replay_completed_text):
|
|
643
|
+
self._responses_text_streamed = True
|
|
586
644
|
yield {"delta": text, "type": "text"}
|
|
587
645
|
return
|
|
588
646
|
|
|
@@ -596,6 +654,7 @@ class RemoteRunner(BaseRunner):
|
|
|
596
654
|
if event_name == "response.reasoning.delta":
|
|
597
655
|
delta = data.get("delta")
|
|
598
656
|
if delta:
|
|
657
|
+
self._responses_reasoning_streamed = True
|
|
599
658
|
yield {"delta": str(delta), "type": "thinking"}
|
|
600
659
|
return
|
|
601
660
|
if event_type in {
|
|
@@ -606,12 +665,16 @@ class RemoteRunner(BaseRunner):
|
|
|
606
665
|
}:
|
|
607
666
|
delta = data.get("delta") or data.get("text")
|
|
608
667
|
if delta:
|
|
668
|
+
self._responses_reasoning_streamed = True
|
|
609
669
|
yield {"delta": str(delta), "type": "thinking"}
|
|
610
670
|
return
|
|
611
|
-
if event_type
|
|
612
|
-
|
|
613
|
-
if
|
|
614
|
-
|
|
671
|
+
if event_type in {"response.output_text.delta", "response.output_text.done"}:
|
|
672
|
+
text = data.get("delta") or data.get("text")
|
|
673
|
+
if text and (
|
|
674
|
+
event_type == "response.output_text.delta" or not self._responses_text_streamed
|
|
675
|
+
):
|
|
676
|
+
self._responses_text_streamed = True
|
|
677
|
+
yield {"delta": str(text), "type": "text"}
|
|
615
678
|
return
|
|
616
679
|
if event_type == "response.output_item.added":
|
|
617
680
|
async for item in self._iter_responses_output_item(data, status="running"):
|
|
@@ -621,13 +684,35 @@ class RemoteRunner(BaseRunner):
|
|
|
621
684
|
async for item in self._iter_responses_output_item(data, status="completed"):
|
|
622
685
|
yield item
|
|
623
686
|
return
|
|
687
|
+
# ksadk/runtime 特有 tool 事件(对标 hosted UI responses-stream.js:176-186)
|
|
688
|
+
if event_type in {"response.tool_call", "response.ksadk.stage_tool_call"}:
|
|
689
|
+
name = str(data.get("name") or data.get("tool_name") or "tool")
|
|
690
|
+
args = self._stringify_responses_payload(data.get("args") if "args" in data else data.get("arguments"))
|
|
691
|
+
cid = str(data.get("call_id") or data.get("item_id") or data.get("run_id") or "")
|
|
692
|
+
yield {"type": "tool_call", "tool_name": name, "tool_args": args, "status": "running", "call_id": cid}
|
|
693
|
+
return
|
|
694
|
+
if event_type in {
|
|
695
|
+
"response.tool_result",
|
|
696
|
+
"response.ksadk.tool_result",
|
|
697
|
+
"response.ksadk.stage_tool_result",
|
|
698
|
+
}:
|
|
699
|
+
name = str(data.get("name") or data.get("tool_name") or "tool")
|
|
700
|
+
output = self._stringify_responses_payload(data.get("output") if "output" in data else data.get("result"))
|
|
701
|
+
cid = str(data.get("call_id") or data.get("item_id") or data.get("run_id") or "")
|
|
702
|
+
yield {
|
|
703
|
+
"type": "tool_result",
|
|
704
|
+
"tool_name": name,
|
|
705
|
+
"tool_output": output,
|
|
706
|
+
"call_id": cid,
|
|
707
|
+
}
|
|
708
|
+
return
|
|
624
709
|
if event_type == "response.function_call_arguments.delta":
|
|
625
710
|
key = str(data.get("item_id") or data.get("call_id") or "")
|
|
626
711
|
name = self._responses_tool_name(key, data)
|
|
627
712
|
args = f"{self._responses_tool_args.get(key, '')}{str(data.get('delta') or '')}"
|
|
628
713
|
if key:
|
|
629
714
|
self._responses_tool_args[key] = args
|
|
630
|
-
yield {"type": "tool_call", "tool_name": name, "tool_args": args, "status": "running"}
|
|
715
|
+
yield {"type": "tool_call", "tool_name": name, "tool_args": args, "status": "running", "call_id": key}
|
|
631
716
|
return
|
|
632
717
|
if event_type == "response.function_call_arguments.done":
|
|
633
718
|
key = str(data.get("item_id") or data.get("call_id") or "")
|
|
@@ -637,12 +722,30 @@ class RemoteRunner(BaseRunner):
|
|
|
637
722
|
)
|
|
638
723
|
if key:
|
|
639
724
|
self._responses_tool_args[key] = args
|
|
640
|
-
yield {"type": "tool_call", "tool_name": name, "tool_args": args, "status": "running"}
|
|
725
|
+
yield {"type": "tool_call", "tool_name": name, "tool_args": args, "status": "running", "call_id": key}
|
|
641
726
|
return
|
|
642
727
|
if event_type == "response.completed":
|
|
643
728
|
response = data.get("response") if isinstance(data.get("response"), dict) else data
|
|
644
729
|
output = response.get("output") if isinstance(response, dict) else None
|
|
645
730
|
if isinstance(output, list):
|
|
731
|
+
replayed_item_keys = set(self._responses_streamed_item_keys)
|
|
732
|
+
replay_completed_text = not self._responses_text_streamed
|
|
733
|
+
replay_completed_reasoning = not self._responses_reasoning_streamed
|
|
734
|
+
for item in output:
|
|
735
|
+
if isinstance(item, dict):
|
|
736
|
+
key = self._responses_item_key(item, {"item": item})
|
|
737
|
+
call_id = str(item.get("call_id") or "")
|
|
738
|
+
if (key and key in replayed_item_keys) or (
|
|
739
|
+
call_id and call_id in replayed_item_keys
|
|
740
|
+
):
|
|
741
|
+
continue
|
|
742
|
+
async for projected in self._iter_responses_output_item(
|
|
743
|
+
{"item": item},
|
|
744
|
+
status="completed",
|
|
745
|
+
replay_completed_text=replay_completed_text,
|
|
746
|
+
replay_completed_reasoning=replay_completed_reasoning,
|
|
747
|
+
):
|
|
748
|
+
yield projected
|
|
646
749
|
chunk = {
|
|
647
750
|
"type": "responses_output",
|
|
648
751
|
"output": output,
|
|
@@ -651,17 +754,39 @@ class RemoteRunner(BaseRunner):
|
|
|
651
754
|
usage = response.get("usage")
|
|
652
755
|
if isinstance(usage, Mapping):
|
|
653
756
|
chunk["usage"] = dict(usage)
|
|
757
|
+
metadata = response.get("metadata")
|
|
758
|
+
if isinstance(metadata, Mapping):
|
|
759
|
+
chunk["metadata"] = dict(metadata)
|
|
654
760
|
yield chunk
|
|
655
761
|
return
|
|
762
|
+
# response.content_part.delta:glm 等模型把 reasoning 走这个事件,
|
|
763
|
+
# partType 含 "reasoning" → thinking(不显示),否则 text(对标 hosted UI)。
|
|
764
|
+
if event_type == "response.content_part.delta":
|
|
765
|
+
part = data.get("part") if isinstance(data.get("part"), dict) else {}
|
|
766
|
+
delta = data.get("delta")
|
|
767
|
+
part_type = str(part.get("type") or (delta.get("type") if isinstance(delta, dict) else "") or data.get("content_type") or "")
|
|
768
|
+
text = ""
|
|
769
|
+
if isinstance(delta, dict):
|
|
770
|
+
text = str(delta.get("text") or delta.get("content") or "")
|
|
771
|
+
elif isinstance(delta, str):
|
|
772
|
+
text = delta
|
|
773
|
+
if not text:
|
|
774
|
+
text = str(data.get("text") or "")
|
|
775
|
+
if not text:
|
|
776
|
+
return
|
|
777
|
+
if "reasoning" in part_type:
|
|
778
|
+
self._responses_reasoning_streamed = True
|
|
779
|
+
yield {"delta": text, "type": "thinking"}
|
|
780
|
+
else:
|
|
781
|
+
self._responses_text_streamed = True
|
|
782
|
+
yield {"delta": text, "type": "text"}
|
|
783
|
+
return
|
|
656
784
|
if event_type == "response.failed":
|
|
657
785
|
yield {"type": "error", "message": self._responses_error_message(data)}
|
|
658
786
|
return
|
|
659
787
|
if event_type == "response.incomplete":
|
|
660
788
|
yield {"type": "error", "message": "Agent 响应未完成"}
|
|
661
789
|
return
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
output_text = RemoteRunner._extract_responses_output_text(data)
|
|
666
|
-
if output_text and event_type != "response.completed":
|
|
667
|
-
yield {"delta": output_text, "type": "text"}
|
|
790
|
+
# 未知事件默认丢弃(对标 hosted UI responses-stream.js: 未知事件 return [])。
|
|
791
|
+
# 之前默认把 data.delta 当 text 显示,会导致 reasoning 走未识别事件时漏进正文。
|
|
792
|
+
# 正文已由 output_text.delta / content_part.delta 显式处理,未知事件不该当正文。
|
ksadk/server/app.py
CHANGED
|
@@ -1064,6 +1064,16 @@ class ListSessionEventsActionRequest(BaseModel):
|
|
|
1064
1064
|
BeforeSeqId: Optional[int] = Field(None, ge=1)
|
|
1065
1065
|
|
|
1066
1066
|
|
|
1067
|
+
class ListSessionMessagesActionRequest(BaseModel):
|
|
1068
|
+
SessionId: str
|
|
1069
|
+
AfterSeqId: Optional[int] = Field(None, ge=0)
|
|
1070
|
+
BeforeSeqId: Optional[int] = Field(None, ge=1)
|
|
1071
|
+
Limit: int = Field(50, ge=1, le=200)
|
|
1072
|
+
IncludeReasoning: bool = False
|
|
1073
|
+
IncludeToolEvents: bool = False
|
|
1074
|
+
IncludeAttachments: bool = True
|
|
1075
|
+
|
|
1076
|
+
|
|
1067
1077
|
class ListSessionCheckpointsActionRequest(BaseModel):
|
|
1068
1078
|
AgentId: str
|
|
1069
1079
|
SessionId: str
|
|
@@ -1596,6 +1606,82 @@ def _apply_checkpoint_resume_audit(
|
|
|
1596
1606
|
return checkpoint
|
|
1597
1607
|
|
|
1598
1608
|
|
|
1609
|
+
def _apply_adk_only_latest_resumable(
|
|
1610
|
+
checkpoints: list[dict[str, Any]],
|
|
1611
|
+
) -> list[dict[str, Any]]:
|
|
1612
|
+
"""P1.4: For ADK invocation_id resume mode, only the latest checkpoint per
|
|
1613
|
+
RunId is independently resumable. Older checkpoints get IsResumable=False."""
|
|
1614
|
+
latest_by_run: dict[str, int] = {}
|
|
1615
|
+
for cp in checkpoints:
|
|
1616
|
+
metadata = cp.get("Metadata") or {}
|
|
1617
|
+
if not metadata.get("only_latest_resumable"):
|
|
1618
|
+
continue
|
|
1619
|
+
run_id = str(cp.get("RunId") or "")
|
|
1620
|
+
seq_id = int(cp.get("SeqId") or 0)
|
|
1621
|
+
if run_id not in latest_by_run or seq_id > latest_by_run[run_id]:
|
|
1622
|
+
latest_by_run[run_id] = seq_id
|
|
1623
|
+
|
|
1624
|
+
for cp in checkpoints:
|
|
1625
|
+
metadata = cp.get("Metadata") or {}
|
|
1626
|
+
if not metadata.get("only_latest_resumable"):
|
|
1627
|
+
continue
|
|
1628
|
+
run_id = str(cp.get("RunId") or "")
|
|
1629
|
+
seq_id = int(cp.get("SeqId") or 0)
|
|
1630
|
+
if seq_id < latest_by_run.get(run_id, 0):
|
|
1631
|
+
if cp.get("IsResumable") is True:
|
|
1632
|
+
cp["IsResumable"] = False
|
|
1633
|
+
cp["ResumeStatus"] = "disabled"
|
|
1634
|
+
cp["ResumeDisabledReason"] = (
|
|
1635
|
+
"新的恢复点已生成,此恢复点暂停恢复能力"
|
|
1636
|
+
)
|
|
1637
|
+
metadata["resume_disabled_reason"] = (
|
|
1638
|
+
"新的恢复点已生成,此恢复点暂停恢复能力"
|
|
1639
|
+
)
|
|
1640
|
+
metadata["resume_status"] = "disabled"
|
|
1641
|
+
cp["Metadata"] = metadata
|
|
1642
|
+
|
|
1643
|
+
return checkpoints
|
|
1644
|
+
|
|
1645
|
+
|
|
1646
|
+
def _check_adk_latest_resumable(
|
|
1647
|
+
checkpoint: dict[str, Any],
|
|
1648
|
+
events: list,
|
|
1649
|
+
) -> dict[str, Any]:
|
|
1650
|
+
"""P1.4: For a single ADK only_latest_resumable checkpoint, verify it is
|
|
1651
|
+
the latest for its RunId. If not, mark IsResumable=False."""
|
|
1652
|
+
metadata = checkpoint.get("Metadata") or {}
|
|
1653
|
+
if not metadata.get("only_latest_resumable"):
|
|
1654
|
+
return checkpoint
|
|
1655
|
+
|
|
1656
|
+
run_id = str(checkpoint.get("RunId") or "")
|
|
1657
|
+
my_seq_id = int(checkpoint.get("SeqId") or 0)
|
|
1658
|
+
|
|
1659
|
+
max_seq_id = my_seq_id
|
|
1660
|
+
for event in events:
|
|
1661
|
+
if event.event_type != "run_checkpoint":
|
|
1662
|
+
continue
|
|
1663
|
+
ev_meta = event.metadata or {}
|
|
1664
|
+
if str(ev_meta.get("run_id") or "") != run_id:
|
|
1665
|
+
continue
|
|
1666
|
+
seq_id = int(event.seq_id or 0)
|
|
1667
|
+
if seq_id > max_seq_id:
|
|
1668
|
+
max_seq_id = seq_id
|
|
1669
|
+
|
|
1670
|
+
if my_seq_id < max_seq_id and checkpoint.get("IsResumable") is True:
|
|
1671
|
+
checkpoint["IsResumable"] = False
|
|
1672
|
+
checkpoint["ResumeStatus"] = "disabled"
|
|
1673
|
+
checkpoint["ResumeDisabledReason"] = (
|
|
1674
|
+
"新的恢复点已生成,此恢复点暂停恢复能力"
|
|
1675
|
+
)
|
|
1676
|
+
metadata["resume_disabled_reason"] = (
|
|
1677
|
+
"新的恢复点已生成,此恢复点暂停恢复能力"
|
|
1678
|
+
)
|
|
1679
|
+
metadata["resume_status"] = "disabled"
|
|
1680
|
+
checkpoint["Metadata"] = metadata
|
|
1681
|
+
|
|
1682
|
+
return checkpoint
|
|
1683
|
+
|
|
1684
|
+
|
|
1599
1685
|
_SIDE_EFFECT_TOOL_NAMES = {
|
|
1600
1686
|
"write_workspace_file",
|
|
1601
1687
|
"write_workspace_files",
|
|
@@ -1729,6 +1815,7 @@ async def _find_session_checkpoint(
|
|
|
1729
1815
|
continue
|
|
1730
1816
|
if checkpoint["CheckpointId"] != checkpoint_id:
|
|
1731
1817
|
continue
|
|
1818
|
+
checkpoint = _check_adk_latest_resumable(checkpoint, events)
|
|
1732
1819
|
return checkpoint
|
|
1733
1820
|
return None
|
|
1734
1821
|
|
|
@@ -1951,6 +2038,12 @@ async def get_agent_ui_bootstrap(request: UiBootstrapRequest):
|
|
|
1951
2038
|
if isinstance(runtime_capabilities, Mapping)
|
|
1952
2039
|
else {},
|
|
1953
2040
|
}
|
|
2041
|
+
checkpoint_resume_supported = bool(checkpoint_resume_capability["Supported"])
|
|
2042
|
+
cancel_run_supported = bool(
|
|
2043
|
+
(runtime_capabilities.get("CancelRun") or {}).get("Supported")
|
|
2044
|
+
if isinstance(runtime_capabilities, Mapping)
|
|
2045
|
+
else False
|
|
2046
|
+
)
|
|
1954
2047
|
return _action_response(
|
|
1955
2048
|
"GetAgentUiBootstrap",
|
|
1956
2049
|
{
|
|
@@ -1966,17 +2059,17 @@ async def get_agent_ui_bootstrap(request: UiBootstrapRequest):
|
|
|
1966
2059
|
"WorkspaceFiles": workspace_enabled,
|
|
1967
2060
|
"Approval": True,
|
|
1968
2061
|
"Thinking": True,
|
|
1969
|
-
"StopRun":
|
|
1970
|
-
"ResumeRun":
|
|
2062
|
+
"StopRun": cancel_run_supported,
|
|
2063
|
+
"ResumeRun": checkpoint_resume_supported,
|
|
1971
2064
|
"RuntimeCapabilities": runtime_capabilities,
|
|
1972
2065
|
"CheckpointResumeCapability": checkpoint_resume_capability,
|
|
1973
2066
|
"RunLifecycle": {
|
|
1974
2067
|
"Enabled": True,
|
|
1975
2068
|
"Resume": True,
|
|
1976
2069
|
"Abort": True,
|
|
1977
|
-
"Checkpoints":
|
|
1978
|
-
"CheckpointResume":
|
|
1979
|
-
"CheckpointResumePreview":
|
|
2070
|
+
"Checkpoints": checkpoint_resume_supported,
|
|
2071
|
+
"CheckpointResume": checkpoint_resume_supported,
|
|
2072
|
+
"CheckpointResumePreview": checkpoint_resume_supported,
|
|
1980
2073
|
},
|
|
1981
2074
|
"MCP": False,
|
|
1982
2075
|
"HostedRuntime": False,
|
|
@@ -2083,6 +2176,51 @@ async def list_session_events_action(request: ListSessionEventsActionRequest):
|
|
|
2083
2176
|
)
|
|
2084
2177
|
|
|
2085
2178
|
|
|
2179
|
+
@app.post("/agentengine/api/v1/ListSessionMessages")
|
|
2180
|
+
async def list_session_messages_action(request: ListSessionMessagesActionRequest):
|
|
2181
|
+
from ksadk.conversations.message_projection import project_session_messages
|
|
2182
|
+
|
|
2183
|
+
service = resolve_session_service()
|
|
2184
|
+
events = await service.get_events(
|
|
2185
|
+
request.SessionId,
|
|
2186
|
+
offset=0,
|
|
2187
|
+
limit=2000,
|
|
2188
|
+
after_seq_id=request.AfterSeqId,
|
|
2189
|
+
before_seq_id=request.BeforeSeqId,
|
|
2190
|
+
)
|
|
2191
|
+
serialized_events = [_event_to_action_payload(event) for event in events]
|
|
2192
|
+
messages = project_session_messages(
|
|
2193
|
+
serialized_events,
|
|
2194
|
+
include_reasoning=request.IncludeReasoning,
|
|
2195
|
+
include_tool_events=request.IncludeToolEvents,
|
|
2196
|
+
include_attachments=request.IncludeAttachments,
|
|
2197
|
+
)
|
|
2198
|
+
if request.AfterSeqId is not None:
|
|
2199
|
+
page = messages
|
|
2200
|
+
has_more = False
|
|
2201
|
+
next_cursor = None
|
|
2202
|
+
else:
|
|
2203
|
+
page = messages[-request.Limit :]
|
|
2204
|
+
minimum_seq_id = int(page[0].get("SeqId") or 0) if page else 0
|
|
2205
|
+
has_more = minimum_seq_id > 1 or len(serialized_events) >= 2000
|
|
2206
|
+
next_cursor = minimum_seq_id - 1 if has_more else None
|
|
2207
|
+
latest_seq_id = (
|
|
2208
|
+
int(page[-1].get("SeqId") or 0)
|
|
2209
|
+
if page
|
|
2210
|
+
else max((int(event.get("SeqId") or 0) for event in serialized_events), default=0)
|
|
2211
|
+
)
|
|
2212
|
+
return _action_response(
|
|
2213
|
+
"ListSessionMessages",
|
|
2214
|
+
{
|
|
2215
|
+
"SessionId": request.SessionId,
|
|
2216
|
+
"Messages": page,
|
|
2217
|
+
"LatestSeqId": latest_seq_id,
|
|
2218
|
+
"HasMore": has_more,
|
|
2219
|
+
"NextCursor": next_cursor,
|
|
2220
|
+
},
|
|
2221
|
+
)
|
|
2222
|
+
|
|
2223
|
+
|
|
2086
2224
|
def _count_resumable_checkpoints(checkpoints: list[dict[str, Any]]) -> int:
|
|
2087
2225
|
"""统计可恢复 checkpoint 数量。
|
|
2088
2226
|
|
|
@@ -2127,6 +2265,7 @@ async def _list_checkpoints_payload(request: ListSessionCheckpointsActionRequest
|
|
|
2127
2265
|
continue
|
|
2128
2266
|
# ResumableTotal 在 OnlyResumable 过滤前统计全量可恢复数(RunId/Framework 范围内)
|
|
2129
2267
|
checkpoints.append(checkpoint)
|
|
2268
|
+
checkpoints = _apply_adk_only_latest_resumable(checkpoints)
|
|
2130
2269
|
resumable_total = _count_resumable_checkpoints(checkpoints)
|
|
2131
2270
|
if request.OnlyResumable:
|
|
2132
2271
|
checkpoints = [cp for cp in checkpoints if cp.get("IsResumable") is True]
|
|
@@ -2202,6 +2341,8 @@ async def get_checkpoint_resume_preview_action(request: GetCheckpointResumePrevi
|
|
|
2202
2341
|
if checkpoint is None:
|
|
2203
2342
|
raise HTTPException(status_code=404, detail="Checkpoint not found")
|
|
2204
2343
|
|
|
2344
|
+
checkpoint = _check_adk_latest_resumable(checkpoint, events)
|
|
2345
|
+
|
|
2205
2346
|
return _action_response(
|
|
2206
2347
|
"GetCheckpointResumePreview",
|
|
2207
2348
|
{"Preview": _build_checkpoint_resume_preview(checkpoint=checkpoint, events=events)},
|
|
@@ -2367,7 +2508,8 @@ async def subscribe_run_events_action(
|
|
|
2367
2508
|
yield "data: [DONE]\n\n"
|
|
2368
2509
|
return
|
|
2369
2510
|
|
|
2370
|
-
# 重连兜底:本轮无新事件时,查全量确认 run 是否已有 terminal
|
|
2511
|
+
# 重连兜底:本轮无新事件时,查全量确认 run 是否已有 terminal
|
|
2512
|
+
# (客户端断连期间 run 已结束)。
|
|
2371
2513
|
# 正常流式期间不触发此查询,保持增量收益。
|
|
2372
2514
|
if not matched_events:
|
|
2373
2515
|
all_events = await service.get_events(session_id)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{bt as e,ct as t,gt as n,lt as r,n as i,t as a,ut as o,wt as s,xt as c}from"./index-
|
|
1
|
+
import{bt as e,ct as t,gt as n,lt as r,n as i,t as a,ut as o,wt as s,xt as c}from"./index-C00Lwg3L.js";var l=s(c(),1),u=t();function d(){let t=e(e=>e.visible),s=e(e=>e.content),c=e(e=>e.type),d=e(e=>e.hide),f=(0,l.useRef)(null);if(i(f),!t||!s)return null;let p=a(s);return(0,u.jsxs)(`aside`,{className:r(`flex h-full flex-shrink-0 flex-col border-l border-slate-200/30 bg-white dark:border-slate-800/40 dark:bg-slate-950`,`w-[min(42rem,50vw)]`),children:[(0,u.jsxs)(`div`,{className:`flex h-14 flex-shrink-0 items-center justify-between border-b border-slate-200/30 px-4 dark:border-slate-800/40`,children:[(0,u.jsxs)(`div`,{className:`flex min-w-0 items-center gap-2`,children:[(0,u.jsx)(`span`,{className:`text-sm font-semibold text-slate-900 dark:text-slate-100`,children:`Artifact`}),(0,u.jsx)(`span`,{className:`rounded-md bg-slate-100 px-1.5 py-0.5 font-mono text-[11px] text-slate-500 dark:bg-slate-900 dark:text-slate-400`,children:c})]}),(0,u.jsxs)(`div`,{className:`flex items-center gap-1.5`,children:[(0,u.jsxs)(`button`,{type:`button`,onClick:()=>{let e=new Blob([s],{type:`text/html;charset=utf-8`}),t=URL.createObjectURL(e),n=document.createElement(`a`);n.href=t,n.download=`artifact.${c===`svg`?`svg`:`html`}`,document.body.appendChild(n),n.click(),document.body.removeChild(n),URL.revokeObjectURL(t)},className:`inline-flex items-center gap-1.5 rounded-lg px-2.5 py-1.5 text-xs font-medium text-slate-600 transition hover:bg-slate-100 dark:text-slate-300 dark:hover:bg-slate-900`,title:`Download`,children:[(0,u.jsx)(n,{className:`h-3.5 w-3.5`}),`Download`]}),(0,u.jsx)(`button`,{type:`button`,onClick:d,className:`rounded-lg p-1.5 text-slate-500 transition hover:bg-slate-100 hover:text-slate-900 dark:text-slate-400 dark:hover:bg-slate-900 dark:hover:text-slate-100`,"aria-label":`Close Artifact`,title:`Close`,children:(0,u.jsx)(o,{className:`h-4 w-4`})})]})]}),(0,u.jsx)(`div`,{className:`min-h-0 flex-1`,children:(0,u.jsx)(`iframe`,{ref:f,srcDoc:p,sandbox:`allow-scripts allow-downloads`,title:`Artifact Preview`,className:`h-full w-full border-0 bg-white`})})]})}export{d as ArtifactsPanel};
|