agentengine-sdk-python 0.8.3__py3-none-any.whl → 0.8.5__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 (819) hide show
  1. {agentengine_sdk_python-0.8.3.dist-info → agentengine_sdk_python-0.8.5.dist-info}/METADATA +13 -9
  2. agentengine_sdk_python-0.8.5.dist-info/RECORD +1296 -0
  3. ksadk/_build_provenance.json +1 -1
  4. ksadk/_process.py +83 -0
  5. ksadk/_str_enum.py +17 -0
  6. ksadk/a2ui/core.py +3 -3
  7. ksadk/api/client.py +111 -91
  8. ksadk/builders/code_builder.py +74 -21
  9. ksadk/builders/framework_requirements.py +22 -3
  10. ksadk/builders/ks3_uploader.py +36 -7
  11. ksadk/builders/managed_runtime_builder.py +1 -0
  12. ksadk/cli/__init__.py +68 -3
  13. ksadk/cli/cmd_config.py +4 -4
  14. ksadk/cli/cmd_create.py +12 -12
  15. ksadk/cli/cmd_hermes.py +24 -2
  16. ksadk/cli/cmd_invoke.py +28 -5
  17. ksadk/cli/cmd_managed_runtime.py +13 -1
  18. ksadk/cli/cmd_openclaw.py +26 -1
  19. ksadk/cli/cmd_plugin.py +76 -13
  20. ksadk/cli/cmd_studio.py +64 -16
  21. ksadk/cli/cmd_web.py +2 -5
  22. ksadk/cli/openclaw_env.py +1 -1
  23. ksadk/cli/resource_common.py +26 -0
  24. ksadk/cli/runtime_bootstrap.py +23 -1
  25. ksadk/codex/client.py +425 -96
  26. ksadk/codex/projection.py +24 -0
  27. ksadk/codex/runtime.py +56 -298
  28. ksadk/common/auth.py +16 -10
  29. ksadk/configs/__init__.py +1 -1
  30. ksadk/configs/env_registry.py +153 -31
  31. ksadk/configs/env_registry_studio.py +86 -0
  32. ksadk/configs/global_config.py +1 -1
  33. ksadk/configs/settings.py +3 -3
  34. ksadk/connector/__init__.py +17 -0
  35. ksadk/connector/channel.py +477 -0
  36. ksadk/context_engine/capabilities.py +52 -4
  37. ksadk/conversations/attachment_storage.py +10 -3
  38. ksadk/conversations/attachments.py +16 -5
  39. ksadk/conversations/message_projection.py +133 -21
  40. ksadk/conversations/normalize.py +28 -10
  41. ksadk/conversations/projector.py +4 -6
  42. ksadk/conversations/runtime_baseline.py +37 -0
  43. ksadk/conversations/runtime_invocation.py +22 -4
  44. ksadk/conversations/runtime_observability.py +39 -10
  45. ksadk/conversations/runtime_payloads.py +9 -0
  46. ksadk/conversations/runtime_persistence.py +105 -9
  47. ksadk/conversations/runtime_preparation.py +42 -33
  48. ksadk/conversations/runtime_resume.py +50 -2
  49. ksadk/conversations/runtime_stream_events.py +34 -35
  50. ksadk/conversations/runtime_streaming.py +33 -7
  51. ksadk/deployment/env_forward.py +2 -2
  52. ksadk/deployment/providers/serverless.py +1 -1
  53. ksadk/events/adapters/_codex_items.py +16 -8
  54. ksadk/events/adapters/_codex_validators.py +18 -7
  55. ksadk/events/adapters/_langgraph_support.py +34 -0
  56. ksadk/events/adapters/langgraph.py +15 -1
  57. ksadk/events/canonical_store.py +3 -1
  58. ksadk/events/projections.py +1 -1
  59. ksadk/events/session_event.py +2 -2
  60. ksadk/harness/__init__.py +14 -0
  61. ksadk/harness/app.py +11 -0
  62. ksadk/harness/artifact_store.py +213 -0
  63. ksadk/harness/capabilities.py +115 -0
  64. ksadk/harness/capability_runtime.py +237 -0
  65. ksadk/harness/closure_contract.py +92 -0
  66. ksadk/harness/compaction_record.py +122 -0
  67. ksadk/harness/compiler.py +284 -0
  68. ksadk/harness/config.py +2 -2
  69. ksadk/harness/conformance/__init__.py +33 -0
  70. ksadk/harness/conformance/contract.py +506 -0
  71. ksadk/harness/conformance/fixtures.py +102 -0
  72. ksadk/harness/context_engine.py +403 -0
  73. ksadk/harness/context_maintenance.py +259 -0
  74. ksadk/harness/context_manifest.py +188 -0
  75. ksadk/harness/context_tuning.py +168 -0
  76. ksadk/harness/draft_runtime.py +322 -0
  77. ksadk/harness/engine/__init__.py +40 -0
  78. ksadk/harness/engine/base.py +122 -0
  79. ksadk/harness/engine/budgets.py +108 -0
  80. ksadk/harness/engine/capability_declarations.py +59 -0
  81. ksadk/harness/engine/completion_guard.py +42 -0
  82. ksadk/harness/engine/context_pipeline.py +579 -0
  83. ksadk/harness/engine/graph_builder.py +859 -0
  84. ksadk/harness/engine/langgraph.py +931 -0
  85. ksadk/harness/engine/mcp_disclosure.py +750 -0
  86. ksadk/harness/engine/policy_runtime.py +70 -0
  87. ksadk/harness/engine/postgres_checkpointer.py +63 -0
  88. ksadk/harness/engine/run_control_bridge.py +80 -0
  89. ksadk/harness/engine/run_types.py +88 -0
  90. ksadk/harness/engine/skill_disclosure.py +189 -0
  91. ksadk/harness/engine/spans.py +41 -0
  92. ksadk/harness/engine/subagents.py +343 -0
  93. ksadk/harness/engine/thread_ids.py +112 -0
  94. ksadk/harness/engine/tool_dispatch.py +144 -0
  95. ksadk/harness/event_tree.py +212 -0
  96. ksadk/harness/events.py +461 -0
  97. ksadk/harness/execution_policy.py +94 -0
  98. ksadk/harness/insights.py +210 -0
  99. ksadk/harness/lifecycle.py +578 -0
  100. ksadk/harness/lifecycle_conformance.py +233 -0
  101. ksadk/harness/lifecycle_local_plane.py +104 -0
  102. ksadk/harness/loop/__init__.py +44 -0
  103. ksadk/harness/loop/reason.py +454 -0
  104. ksadk/harness/loop/tools.py +762 -0
  105. ksadk/harness/managed_runtime.py +884 -0
  106. ksadk/harness/mcp_matrix.py +474 -0
  107. ksadk/harness/mcp_runtime.py +471 -0
  108. ksadk/harness/memory_consolidation.py +157 -0
  109. ksadk/harness/memory_loop.py +156 -0
  110. ksadk/harness/memory_runtime.py +541 -0
  111. ksadk/harness/metrics.py +90 -0
  112. ksadk/harness/model_capability.py +253 -0
  113. ksadk/harness/model_provider.py +220 -0
  114. ksadk/harness/observability/__init__.py +29 -0
  115. ksadk/harness/observability/inspection.py +528 -0
  116. ksadk/harness/observability/metrics.py +167 -0
  117. ksadk/harness/observability/otel_exporter.py +218 -0
  118. ksadk/harness/prompt_cache.py +70 -0
  119. ksadk/harness/public_activity.py +154 -0
  120. ksadk/harness/readiness.py +249 -0
  121. ksadk/harness/reasoner.py +466 -13
  122. ksadk/harness/release_readiness.py +140 -0
  123. ksadk/harness/resource_ref.py +69 -0
  124. ksadk/harness/run_control.py +668 -0
  125. ksadk/harness/runtime.py +171 -136
  126. ksadk/harness/runtime_capabilities.py +158 -0
  127. ksadk/harness/runtime_server.py +514 -0
  128. ksadk/harness/sandbox_adapters.py +739 -0
  129. ksadk/harness/sandbox_backend.py +530 -0
  130. ksadk/harness/sandbox_conformance.py +288 -0
  131. ksadk/harness/sandbox_lease.py +77 -0
  132. ksadk/harness/sandbox_lease_conformance.py +259 -0
  133. ksadk/harness/sandbox_matrix.py +170 -0
  134. ksadk/harness/sandbox_reconciliation.py +140 -0
  135. ksadk/harness/sandbox_recovery.py +586 -0
  136. ksadk/harness/sandbox_recovery_conformance.py +246 -0
  137. ksadk/harness/session_store.py +243 -0
  138. ksadk/harness/skill_composition.py +212 -0
  139. ksadk/harness/skill_feedback.py +123 -0
  140. ksadk/harness/skill_matrix.py +233 -0
  141. ksadk/harness/skill_runtime.py +259 -0
  142. ksadk/harness/spec.py +332 -0
  143. ksadk/harness/state.py +153 -0
  144. ksadk/harness/strategies.py +149 -0
  145. ksadk/harness/subagent.py +253 -0
  146. ksadk/harness/tool_policy.py +131 -0
  147. ksadk/harness/tool_receipts.py +96 -0
  148. ksadk/harness/tool_reliability.py +81 -0
  149. ksadk/harness/tools.py +38 -13
  150. ksadk/harness/user_errors.py +69 -0
  151. ksadk/harness/working_context.py +81 -0
  152. ksadk/harness/working_context_patch.py +99 -0
  153. ksadk/identity/resolver.py +18 -0
  154. ksadk/interaction/__init__.py +1 -1
  155. ksadk/interaction/ledger.py +1 -1
  156. ksadk/interaction/provider.py +2 -1
  157. ksadk/interaction/providers/__init__.py +3 -1
  158. ksadk/interaction/providers/adk.py +1 -1
  159. ksadk/interaction/providers/codex.py +1 -1
  160. ksadk/interaction/providers/harness.py +50 -0
  161. ksadk/interaction/providers/langgraph.py +2 -1
  162. ksadk/kernel/__init__.py +10 -0
  163. ksadk/kernel/approval_presentation.py +91 -0
  164. ksadk/kernel/authorization.py +1 -1
  165. ksadk/kernel/bootstrap.py +114 -106
  166. ksadk/kernel/contract_fingerprints.py +1 -1
  167. ksadk/kernel/contracts.py +5 -0
  168. ksadk/kernel/control.py +32 -1
  169. ksadk/kernel/execution_grants.py +221 -0
  170. ksadk/kernel/execution_grants_memory.py +133 -0
  171. ksadk/kernel/execution_grants_postgres.py +217 -0
  172. ksadk/kernel/execution_grants_sqlite.py +209 -0
  173. ksadk/kernel/ingress.py +42 -29
  174. ksadk/kernel/mapping.py +1 -1
  175. ksadk/kernel/memory_store.py +24 -11
  176. ksadk/kernel/postgres_store.py +63 -33
  177. ksadk/kernel/recovery.py +78 -14
  178. ksadk/kernel/sql/001_agent_kernel.sql +23 -0
  179. ksadk/kernel/sqlite_store.py +331 -25
  180. ksadk/kernel/state.py +2 -3
  181. ksadk/kernel/store.py +72 -2
  182. ksadk/kernel/worker.py +144 -51
  183. ksadk/kernel/worker_history.py +72 -0
  184. ksadk/kernel/worker_identity.py +47 -0
  185. ksadk/knowledge_base/client.py +57 -27
  186. ksadk/mcp_runtime/__init__.py +12 -4
  187. ksadk/memory/adk/backends/sdk_ltm_backend.py +161 -41
  188. ksadk/memory/adk/resilient_session_service.py +25 -17
  189. ksadk/memory/adk/short_term_memory.py +26 -9
  190. ksadk/memory/coordinator.py +22 -6
  191. ksadk/memory/extraction.py +40 -1
  192. ksadk/memory/models.py +84 -5
  193. ksadk/memory/provider_resolver.py +8 -0
  194. ksadk/memory/providers/local_postgres.py +200 -0
  195. ksadk/memory/providers/local_sqlite.py +42 -3
  196. ksadk/memory/retrieval.py +189 -0
  197. ksadk/memory/store_adapter.py +150 -0
  198. ksadk/model_policy.py +2 -2
  199. ksadk/model_proxy/detect.py +254 -0
  200. ksadk/model_proxy/server.py +14 -7
  201. ksadk/model_proxy/transform.py +40 -2
  202. ksadk/plugins/__init__.py +5 -1
  203. ksadk/plugins/artifacts.py +275 -0
  204. ksadk/plugins/bridges/__init__.py +4 -2
  205. ksadk/plugins/bridges/codex.py +120 -13
  206. ksadk/plugins/bridges/dsh.py +351 -156
  207. ksadk/plugins/builtins.py +72 -35
  208. ksadk/plugins/bundle_security.py +28 -4
  209. ksadk/plugins/codex_manifest.py +983 -0
  210. ksadk/plugins/companion_artifacts.py +83 -0
  211. ksadk/plugins/companions.py +368 -0
  212. ksadk/plugins/composition.py +8 -0
  213. ksadk/plugins/contracts.py +119 -1
  214. ksadk/plugins/delivery.py +153 -0
  215. ksadk/plugins/dsh_channels.py +156 -0
  216. ksadk/plugins/dsh_home.py +140 -0
  217. ksadk/plugins/dsh_installation_digest.py +71 -0
  218. ksadk/plugins/dsh_teams.py +105 -0
  219. ksadk/plugins/dsh_toolchain.py +203 -32
  220. ksadk/plugins/ecosystem_probe.py +6 -6
  221. ksadk/plugins/execution_host.py +99 -0
  222. ksadk/plugins/host.py +102 -39
  223. ksadk/plugins/kernel_adapter.py +205 -0
  224. ksadk/plugins/providers/__init__.py +23 -0
  225. ksadk/plugins/providers/bundles/dsh-channels-client/client.js +19 -0
  226. ksadk/plugins/providers/bundles/dsh-channels-client/cordis.patch.yml +3 -0
  227. ksadk/plugins/providers/bundles/dsh-channels-client/index.mjs +7 -0
  228. ksadk/plugins/providers/bundles/dsh-channels-client/package.json +12 -0
  229. ksadk/plugins/providers/bundles/dsh-knowledge/cordis.patch.yml +3 -0
  230. ksadk/plugins/providers/bundles/dsh-knowledge/index.mjs +53 -0
  231. ksadk/plugins/providers/bundles/dsh-knowledge/package.json +16 -0
  232. ksadk/plugins/providers/bundles/dsh-memory/cordis.patch.yml +3 -0
  233. ksadk/plugins/providers/bundles/dsh-memory/index.mjs +175 -0
  234. ksadk/plugins/providers/bundles/dsh-memory/package.json +16 -0
  235. ksadk/plugins/providers/bundles/dsh-platform-resources/client.mjs +166 -0
  236. ksadk/plugins/providers/bundles/dsh-platform-resources/cordis.patch.yml +3 -0
  237. ksadk/plugins/providers/bundles/dsh-platform-resources/index.mjs +9 -0
  238. ksadk/plugins/providers/bundles/dsh-platform-resources/package.json +12 -0
  239. ksadk/plugins/providers/bundles/dsh-skill-center/cordis.patch.yml +3 -0
  240. ksadk/plugins/providers/bundles/dsh-skill-center/index.mjs +125 -0
  241. ksadk/plugins/providers/bundles/dsh-skill-center/package.json +16 -0
  242. ksadk/plugins/providers/bundles/dsh-teams-client/client.js +29 -0
  243. ksadk/plugins/providers/bundles/dsh-teams-client/cordis.patch.yml +3 -0
  244. ksadk/plugins/providers/bundles/dsh-teams-client/index.mjs +11 -0
  245. ksadk/plugins/providers/bundles/dsh-teams-client/package.json +35 -0
  246. ksadk/plugins/providers/bundles/dsh-teams-leader-policy/cordis.patch.yml +3 -0
  247. ksadk/plugins/providers/bundles/dsh-teams-leader-policy/index.mjs +13 -0
  248. ksadk/plugins/providers/bundles/dsh-teams-leader-policy/package.json +27 -0
  249. ksadk/plugins/providers/bundles/dsh-teams-profile/cordis.patch.yml +3 -0
  250. ksadk/plugins/providers/bundles/dsh-teams-profile/index.mjs +12 -0
  251. ksadk/plugins/providers/bundles/dsh-teams-profile/package.json +26 -0
  252. ksadk/plugins/providers/bundles/dsh-teams-router/cordis.patch.yml +3 -0
  253. ksadk/plugins/providers/bundles/dsh-teams-router/index.mjs +16 -0
  254. ksadk/plugins/providers/bundles/dsh-teams-router/package.json +27 -0
  255. ksadk/plugins/providers/bundles/dsh-teams-tasks/cordis.patch.yml +3 -0
  256. ksadk/plugins/providers/bundles/dsh-teams-tasks/index.mjs +16 -0
  257. ksadk/plugins/providers/bundles/dsh-teams-tasks/package.json +27 -0
  258. ksadk/plugins/providers/bundles/dsh-teams-tools/cordis.patch.yml +3 -0
  259. ksadk/plugins/providers/bundles/dsh-teams-tools/index.mjs +25 -0
  260. ksadk/plugins/providers/bundles/dsh-teams-tools/package.json +29 -0
  261. ksadk/plugins/providers/bundles/dsh-teams-tools/tools.json +235 -0
  262. ksadk/plugins/providers/bundles/ksadk-codex/index.mjs +1 -1
  263. ksadk/plugins/providers/bundles/ksadk-dsh-capability-host/cordis.patch.yml +3 -0
  264. ksadk/plugins/providers/bundles/ksadk-dsh-capability-host/index.mjs +920 -0
  265. ksadk/plugins/providers/bundles/ksadk-dsh-capability-host/package.json +27 -0
  266. ksadk/plugins/providers/bundles/ksadk-dsh-companion-host/client.mjs +133 -0
  267. ksadk/plugins/providers/bundles/ksadk-dsh-companion-host/index.mjs +14 -0
  268. ksadk/plugins/providers/bundles/ksadk-dsh-studio/client.js +115 -0
  269. ksadk/plugins/providers/bundles/ksadk-dsh-studio/index.mjs +19 -0
  270. ksadk/plugins/providers/bundles/ksadk-dsh-studio/package.json +7 -0
  271. ksadk/plugins/providers/bundles/ksadk-harness/index.mjs +1 -1
  272. ksadk/plugins/providers/codex.py +344 -70
  273. ksadk/plugins/providers/codex_native.py +368 -0
  274. ksadk/plugins/providers/codex_turn.py +301 -0
  275. ksadk/plugins/providers/dsh_capabilities.py +1450 -0
  276. ksadk/plugins/providers/dsh_mcp.py +467 -0
  277. ksadk/plugins/providers/harness.py +294 -141
  278. ksadk/plugins/providers/harness_delegation.py +593 -0
  279. ksadk/plugins/providers/harness_managed.py +456 -0
  280. ksadk/plugins/providers/harness_tools.py +326 -0
  281. ksadk/plugins/providers/mcp_projection.py +150 -0
  282. ksadk/plugins/providers/platform_resources.py +657 -0
  283. ksadk/plugins/providers/shipped_dsh.py +4 -1
  284. ksadk/plugins/resolver.py +8 -1
  285. ksadk/plugins/subagent_providers/codex.py +28 -6
  286. ksadk/plugins/teams/__init__.py +5 -0
  287. ksadk/plugins/teams/api.py +405 -0
  288. ksadk/plugins/teams/application.py +680 -0
  289. ksadk/plugins/teams/artifacts.py +60 -0
  290. ksadk/plugins/teams/context.py +96 -0
  291. ksadk/plugins/teams/contracts.py +136 -0
  292. ksadk/plugins/teams/domain.py +1427 -0
  293. ksadk/plugins/teams/domain_values.py +24 -0
  294. ksadk/plugins/teams/errors.py +13 -0
  295. ksadk/plugins/teams/migration.py +311 -0
  296. ksadk/plugins/teams/migration_cli.py +322 -0
  297. ksadk/plugins/teams/postgres_store.py +138 -0
  298. ksadk/plugins/teams/runtime.py +777 -0
  299. ksadk/plugins/teams/source_projection.py +84 -0
  300. ksadk/plugins/teams/store.py +311 -0
  301. ksadk/plugins/teams/task_decisions.py +423 -0
  302. ksadk/plugins/teams/transport.py +91 -0
  303. ksadk/plugins/teams/workspaces.py +255 -0
  304. ksadk/resource_runtime/__init__.py +4 -0
  305. ksadk/resource_runtime/broker.py +616 -0
  306. ksadk/resource_runtime/build_artifacts.py +165 -0
  307. ksadk/resource_runtime/contracts.py +138 -0
  308. ksadk/resource_runtime/discovery_receipts.py +100 -0
  309. ksadk/resource_runtime/discovery_skills.py +172 -0
  310. ksadk/resource_runtime/errors.py +6 -0
  311. ksadk/resource_runtime/ipc.py +142 -0
  312. ksadk/resource_runtime/kernel_approval.py +165 -0
  313. ksadk/resource_runtime/knowledge.py +114 -0
  314. ksadk/resource_runtime/langgraph.py +94 -0
  315. ksadk/resource_runtime/leases.py +191 -0
  316. ksadk/resource_runtime/managed_projection.py +186 -0
  317. ksadk/resource_runtime/mcp_server.py +138 -0
  318. ksadk/resource_runtime/memory_provider.py +318 -0
  319. ksadk/resource_runtime/memory_receipts.py +34 -0
  320. ksadk/resource_runtime/operation_ledger.py +211 -0
  321. ksadk/resource_runtime/plugin_config.py +48 -0
  322. ksadk/resource_runtime/policy_authorization.py +54 -0
  323. ksadk/resource_runtime/process.py +110 -0
  324. ksadk/resource_runtime/skill_artifacts.py +153 -0
  325. ksadk/resource_runtime/skill_runtime.py +42 -0
  326. ksadk/resource_runtime/skill_workspace.py +118 -0
  327. ksadk/resource_runtime/skills.py +215 -0
  328. ksadk/resource_runtime/snapshots.py +180 -0
  329. ksadk/resource_runtime/socket_server.py +103 -0
  330. ksadk/resource_runtime/supervisor.py +380 -0
  331. ksadk/resource_runtime/worker.py +504 -0
  332. ksadk/runners/_langgraph_runner_streams.py +182 -38
  333. ksadk/runners/_langgraph_state_reader.py +106 -0
  334. ksadk/runners/_session_identity.py +285 -0
  335. ksadk/runners/adk_runner.py +166 -63
  336. ksadk/runners/base_runner.py +29 -1
  337. ksadk/runners/langgraph_runner.py +542 -195
  338. ksadk/runners/patch_langchain.py +47 -0
  339. ksadk/runners/usage_accumulator.py +11 -3
  340. ksadk/runtime/_runner_adapter/stream_mapping.py +194 -20
  341. ksadk/runtime/adapter.py +2 -0
  342. ksadk/runtime/conversation_execution.py +117 -17
  343. ksadk/runtime/executor.py +87 -3
  344. ksadk/runtime/factory.py +65 -274
  345. ksadk/runtime/framework_adapters.py +23 -0
  346. ksadk/runtime/preprocessing.py +21 -5
  347. ksadk/runtime/runner_adapter.py +106 -18
  348. ksadk/runtime/skill_eval_result.py +36 -0
  349. ksadk/runtime/timing.py +46 -0
  350. ksadk/runtime/usage.py +49 -18
  351. ksadk/runtime_context.py +142 -2
  352. ksadk/sandbox/backends/e2b.py +288 -25
  353. ksadk/sandbox/backends/local_process.py +72 -39
  354. ksadk/sandbox/base.py +64 -1
  355. ksadk/sandbox/diagnostics.py +82 -0
  356. ksadk/sandbox/e2b_connection.py +66 -0
  357. ksadk/sandbox/local_controls.py +746 -0
  358. ksadk/sandbox/local_launcher.py +90 -0
  359. ksadk/scheduler/dispatcher.py +1 -1
  360. ksadk/scheduler/sqlite_store.py +18 -0
  361. ksadk/server/composition.py +2 -0
  362. ksadk/server/factory.py +5 -0
  363. ksadk/server/invocation_identity.py +118 -0
  364. ksadk/server/persistence_capability.py +516 -0
  365. ksadk/server/routes/common.py +32 -3
  366. ksadk/server/routes/control.py +271 -52
  367. ksadk/server/routes/dependencies.py +16 -0
  368. ksadk/server/routes/kernel_ingress.py +9 -1
  369. ksadk/server/routes/misc.py +233 -25
  370. ksadk/server/routes/models.py +47 -4
  371. ksadk/server/routes/openai_compat.py +76 -13
  372. ksadk/server/routes/projection.py +252 -12
  373. ksadk/server/routes/run.py +106 -35
  374. ksadk/server/routes/sessions.py +491 -138
  375. ksadk/server/routes/streaming.py +43 -9
  376. ksadk/server/routes/workspace.py +130 -21
  377. ksadk/server/static/assets/{ArtifactsPanel-10GPXIJ6.js → ArtifactsPanel-C1YoaKhx.js} +1 -1
  378. ksadk/server/static/assets/{CodeBlock-BXk1l-G1.js → CodeBlock-DHuAWfr5.js} +10 -7
  379. ksadk/server/static/assets/MathMessageMarkdown-cvdrBwYr.js +8 -0
  380. ksadk/server/static/assets/{MermaidBlock--OEYoXIJ.js → MermaidBlock-DeBkbJvI.js} +9 -9
  381. ksadk/server/static/assets/{NativeTerminalPanel-DrEe1syq.js → NativeTerminalPanel-oJj44_sV.js} +1 -1
  382. ksadk/server/static/assets/{abnfDiagram-VCTEODGH-eeTFJLvo.js → abnfDiagram-VCTEODGH-Dhfv25o2.js} +1 -1
  383. ksadk/server/static/assets/{arc-S3ZNbIAd.js → arc-C35w-pn1.js} +1 -1
  384. ksadk/server/static/assets/architecture-7GRP2DOG-BCsa26zi.js +1 -0
  385. ksadk/server/static/assets/{architectureDiagram-5GKGNRK7-CRjgEJDX.js → architectureDiagram-5GKGNRK7-FoMY_u6D.js} +1 -1
  386. ksadk/server/static/assets/{blockDiagram-NRAW4CY4-C7bsBoNH.js → blockDiagram-NRAW4CY4-zD4Rt7oE.js} +1 -1
  387. ksadk/server/static/assets/{c4Diagram-UCG6FXSJ-BX3c33Ob.js → c4Diagram-UCG6FXSJ-BCtGGkhN.js} +1 -1
  388. ksadk/server/static/assets/channel-_KDifNys.js +1 -0
  389. ksadk/server/static/assets/chunk-2Q5K7J3B-HdVkJ9F0.js +1 -0
  390. ksadk/server/static/assets/{chunk-5VM5RSS4-bZ7ws-8m.js → chunk-5VM5RSS4-C2Ms13_k.js} +1 -1
  391. ksadk/server/static/assets/{chunk-F27PBJKO-D2r0pvhY.js → chunk-F27PBJKO-BoTj-qgx.js} +1 -1
  392. ksadk/server/static/assets/{chunk-G27WJ6UU-CZwF3oUP.js → chunk-G27WJ6UU-DOMyyjHH.js} +1 -1
  393. ksadk/server/static/assets/{chunk-JWPE2WC7-DigFYCML.js → chunk-JWPE2WC7-DbJJqDli.js} +1 -1
  394. ksadk/server/static/assets/{chunk-LCL6LL3I-CnorGV5q.js → chunk-LCL6LL3I-CLuRCt_W.js} +1 -1
  395. ksadk/server/static/assets/{chunk-POPQ4Y6H-CexntQA-.js → chunk-POPQ4Y6H-D0Dz847e.js} +1 -1
  396. ksadk/server/static/assets/{chunk-RHFEMEQ7-CHTC7UxA.js → chunk-RHFEMEQ7-BWSRzEcd.js} +1 -1
  397. ksadk/server/static/assets/{chunk-SVP7TREG-BC1EWt_-.js → chunk-SVP7TREG-CPoHqkCY.js} +1 -1
  398. ksadk/server/static/assets/{chunk-XXDRQBXY-BTG24xN0.js → chunk-XXDRQBXY-Bx1jS-X-.js} +1 -1
  399. ksadk/server/static/assets/classDiagram-DTDB5LWJ-BVCW3ZqZ.js +1 -0
  400. ksadk/server/static/assets/classDiagram-v2-JRS7N3AN-BVCW3ZqZ.js +1 -0
  401. ksadk/server/static/assets/{cose-bilkent-JH36ORCC-CkonRdAS.js → cose-bilkent-JH36ORCC-LzyWm4m0.js} +1 -1
  402. ksadk/server/static/assets/cynefin-OW5HDTMX-CNXjFctL.js +1 -0
  403. ksadk/server/static/assets/{cynefinDiagram-5FMLGOSQ-DEOCBe15.js → cynefinDiagram-5FMLGOSQ-BlW_bHED.js} +1 -1
  404. ksadk/server/static/assets/{dagre-3AP2YEHR-DBThPuLa.js → dagre-3AP2YEHR-Bu_S86_i.js} +1 -1
  405. ksadk/server/static/assets/{dagre-ClTPrTht.js → dagre-B7nCawed.js} +1 -1
  406. ksadk/server/static/assets/{diagram-S7CK7UJ4-DsZHrnpJ.js → diagram-S7CK7UJ4-W_Zow2Km.js} +1 -1
  407. ksadk/server/static/assets/{diagram-UQ7AKVKN-BJR4nhVr.js → diagram-UQ7AKVKN-CLIKAHON.js} +1 -1
  408. ksadk/server/static/assets/{diagram-VSXAHHWV-xzcERX0a.js → diagram-VSXAHHWV-Dv6_QAlN.js} +1 -1
  409. ksadk/server/static/assets/{diagram-VX7I27RA-tn2xM1xu.js → diagram-VX7I27RA-BWAWRDtZ.js} +1 -1
  410. ksadk/server/static/assets/{diagram-Z3DM3KII-BZPGBkw5.js → diagram-Z3DM3KII-XA_rWMss.js} +1 -1
  411. ksadk/server/static/assets/dist-0X_j2Rcc.js +1 -0
  412. ksadk/server/static/assets/{dist-4zN1_tq5.js → dist-1nXCdsej.js} +1 -1
  413. ksadk/server/static/assets/{dist-M67Hwu0n.js → dist-2vL2sAdF.js} +1 -1
  414. ksadk/server/static/assets/{dist-BJLUwH6W.js → dist-B3bA05-e.js} +1 -1
  415. ksadk/server/static/assets/{dist-8VWI-2bZ.js → dist-BLpRy_Yx.js} +1 -1
  416. ksadk/server/static/assets/{dist-D1V3f1B4.js → dist-BO_UwatH.js} +1 -1
  417. ksadk/server/static/assets/{dist-Cq4OL6nO.js → dist-BdNFAney.js} +1 -1
  418. ksadk/server/static/assets/{dist-DfGFrb0v.js → dist-C0J5_3qM.js} +1 -1
  419. ksadk/server/static/assets/{dist-DyqV4fL4.js → dist-CGt_UGeO.js} +1 -1
  420. ksadk/server/static/assets/{dist-DjhwGUgZ.js → dist-CIRMvUM4.js} +1 -1
  421. ksadk/server/static/assets/{dist-B1oWRmrH.js → dist-CIpWk_5f.js} +1 -1
  422. ksadk/server/static/assets/{dist-CXpH_Rdd.js → dist-CPyieXEf.js} +1 -1
  423. ksadk/server/static/assets/{dist-Btyo4vX4.js → dist-D-92S-Fd.js} +1 -1
  424. ksadk/server/static/assets/{dist-u8tVH5Ak.js → dist-DNKuMzWc.js} +1 -1
  425. ksadk/server/static/assets/{dist-DQAB0qn_.js → dist-DWfdmz9s.js} +2 -2
  426. ksadk/server/static/assets/{dist-B-46q3Hk.js → dist-DppE_r-D.js} +1 -1
  427. ksadk/server/static/assets/{dist-C71CsujO.js → dist-DyG6Ya-z.js} +1 -1
  428. ksadk/server/static/assets/{dist-DJmCPRVB.js → dist-M-SrdhhZ.js} +1 -1
  429. ksadk/server/static/assets/{dist-Bbwqa8TG.js → dist-mqD5TZup.js} +1 -1
  430. ksadk/server/static/assets/{dist-BI1qQH4n.js → dist-n3n6t_FO.js} +1 -1
  431. ksadk/server/static/assets/{dist-DUwKa_l6.js → dist-qe8xjesy.js} +1 -1
  432. ksadk/server/static/assets/{dist-B0gdk4rl.js → dist-t6MzE0jU.js} +1 -1
  433. ksadk/server/static/assets/{ebnfDiagram-PWID7BFC-DVfMsLge.js → ebnfDiagram-PWID7BFC-BfCzF1Xi.js} +1 -1
  434. ksadk/server/static/assets/{erDiagram-SSCWMZ5O-CWEZSYaw.js → erDiagram-SSCWMZ5O-Dpz9_0zn.js} +1 -1
  435. ksadk/server/static/assets/eventmodeling-NTZA5JFV-B5-8RH9c.js +1 -0
  436. ksadk/server/static/assets/flowDiagram-A5DVABFB-BppFxEKw.js +1 -0
  437. ksadk/server/static/assets/{ganttDiagram-EL5Y4UJY-TWIxoyYL.js → ganttDiagram-EL5Y4UJY-DDaG1f4_.js} +1 -1
  438. ksadk/server/static/assets/gitGraph-4MIJSDKK-B-AhYj4C.js +1 -0
  439. ksadk/server/static/assets/{gitGraphDiagram-WWUBYQGX-DDh2hd2S.js → gitGraphDiagram-WWUBYQGX-DcxzRwYC.js} +1 -1
  440. ksadk/server/static/assets/index-BMd2mucF.css +2 -0
  441. ksadk/server/static/assets/index-D5qyyhTH.js +244 -0
  442. ksadk/server/static/assets/info-A6RAGUB7-0uiKrC35.js +1 -0
  443. ksadk/server/static/assets/{infoDiagram-RXCK75RN-CgoyWiDR.js → infoDiagram-RXCK75RN-CYMdn6lQ.js} +1 -1
  444. ksadk/server/static/assets/{ishikawaDiagram-5VMMS53U-CyRIu_om.js → ishikawaDiagram-5VMMS53U-CLHFAD6c.js} +1 -1
  445. ksadk/server/static/assets/{journeyDiagram-EYS64GPL-DN5JXHzN.js → journeyDiagram-EYS64GPL-BHSKQGAl.js} +1 -1
  446. ksadk/server/static/assets/{kanban-definition-3QL26DDD-CNP3ui66.js → kanban-definition-3QL26DDD-D5W3tjTT.js} +1 -1
  447. ksadk/server/static/assets/{katex-DWEzQy1d.js → katex-mtjOi4KM.js} +1 -1
  448. ksadk/server/static/assets/{linear-C-qHp7d2.js → linear-BETqsxE_.js} +1 -1
  449. ksadk/server/static/assets/{mermaid-parser.core-Cl-K943T.js → mermaid-parser.core-DPWrvs90.js} +2 -2
  450. ksadk/server/static/assets/{mindmap-definition-FBJOCRG2-D5DB5mYm.js → mindmap-definition-FBJOCRG2-CJG2mfBP.js} +1 -1
  451. ksadk/server/static/assets/packet-AYTQ26CC-BVTmZ_cw.js +1 -0
  452. ksadk/server/static/assets/{pegDiagram-XKGWAZYB-BQVc9ia_.js → pegDiagram-XKGWAZYB-GjAPNaqn.js} +1 -1
  453. ksadk/server/static/assets/pie-WAS4IAKB-DyfxBU7V.js +1 -0
  454. ksadk/server/static/assets/{pieDiagram-E7YTZNPT-B6UCAvtY.js → pieDiagram-E7YTZNPT-ToIewrqr.js} +1 -1
  455. ksadk/server/static/assets/{quadrantDiagram-AXDQQJYC-mIr2d3eb.js → quadrantDiagram-AXDQQJYC-4d78KiEY.js} +1 -1
  456. ksadk/server/static/assets/radar-RG4KPBEZ-FFontjL2.js +1 -0
  457. ksadk/server/static/assets/railroad-74A4TZTK-D0Uidzen.js +1 -0
  458. ksadk/server/static/assets/railroad-abnf-HS5TGJTU-BOo_9T2h.js +1 -0
  459. ksadk/server/static/assets/railroad-ebnf-LZEXJU2U-D6-BjW7Z.js +1 -0
  460. ksadk/server/static/assets/railroad-peg-WCYAUIDC-CXx__yMm.js +1 -0
  461. ksadk/server/static/assets/{railroadDiagram-O6MQD6OU-Dt3FFDmK.js → railroadDiagram-O6MQD6OU-DKzx4xfv.js} +1 -1
  462. ksadk/server/static/assets/{requirementDiagram-EFPCY7ZU-Cz_vQtgF.js → requirementDiagram-EFPCY7ZU-CJYljxQS.js} +1 -1
  463. ksadk/server/static/assets/{sankeyDiagram-P5KCCOFB-wlO1TsH5.js → sankeyDiagram-P5KCCOFB-DLU2fV1w.js} +1 -1
  464. ksadk/server/static/assets/{sequenceDiagram-WJ2MYXX4-CHRbElYq.js → sequenceDiagram-WJ2MYXX4-CVqysrA9.js} +1 -1
  465. ksadk/server/static/assets/{sizeCapture-X5ZJPWSS-DXX54iyO.js → sizeCapture-X5ZJPWSS-BUxfmTHc.js} +1 -1
  466. ksadk/server/static/assets/{stateDiagram-HBIQ2CUA-Dp_lw9II.js → stateDiagram-HBIQ2CUA-CNFgYI9E.js} +1 -1
  467. ksadk/server/static/assets/stateDiagram-v2-4QOOHH4V-BXAFScz8.js +1 -0
  468. ksadk/server/static/assets/{swimlanes-XN3QIQJK-D8O5UlLG.js → swimlanes-XN3QIQJK-B6qCMe8N.js} +1 -1
  469. ksadk/server/static/assets/swimlanesDiagram-VK2B7HYN-uIQ2lJKH.js +8 -0
  470. ksadk/server/static/assets/{timeline-definition-24CTP7MA-8mfKcxvG.js → timeline-definition-24CTP7MA-CMD3x_U_.js} +1 -1
  471. ksadk/server/static/assets/treeView-Q6P3EWNA-CK5GgucZ.js +1 -0
  472. ksadk/server/static/assets/treemap-WGGIJYW6-Bq1u1SpG.js +1 -0
  473. ksadk/server/static/assets/{vennDiagram-4TSXK5OY-EnNl0DE7.js → vennDiagram-4TSXK5OY-BqtUqvXM.js} +1 -1
  474. ksadk/server/static/assets/wardley-WFR3VGLG-QAt06uEz.js +1 -0
  475. ksadk/server/static/assets/{wardleyDiagram-VM6X3IG4-DBu5fbYr.js → wardleyDiagram-VM6X3IG4-B5KfDjI-.js} +1 -1
  476. ksadk/server/static/assets/{xychartDiagram-S5SC5T6Z-BSVXs4IY.js → xychartDiagram-S5SC5T6Z-BaUlSZrn.js} +1 -1
  477. ksadk/server/static/index.html +2 -2
  478. ksadk/server/terminal_sessions.py +34 -2
  479. ksadk/session_context.py +66 -0
  480. ksadk/sessions/__init__.py +11 -21
  481. ksadk/sessions/_local_service_sync.py +263 -1
  482. ksadk/sessions/base.py +176 -0
  483. ksadk/sessions/errors.py +4 -0
  484. ksadk/sessions/in_memory.py +309 -19
  485. ksadk/sessions/invocation_identity.py +235 -0
  486. ksadk/sessions/local_service.py +83 -1
  487. ksadk/sessions/persistence.py +275 -0
  488. ksadk/sessions/postgres_service.py +358 -0
  489. ksadk/sessions/resilience.py +8 -0
  490. ksadk/sessions/resilient.py +362 -4
  491. ksadk/sessions/topology.py +135 -0
  492. ksadk/skills/__init__.py +26 -0
  493. ksadk/skills/events.py +464 -0
  494. ksadk/skills/manifest_cache.py +263 -0
  495. ksadk/skills/mcp_server/__init__.py +0 -0
  496. ksadk/skills/mcp_server/_registry.py +33 -0
  497. ksadk/skills/mcp_server/_routing.py +26 -0
  498. ksadk/skills/mcp_server/_workspace_files.py +160 -0
  499. ksadk/skills/mcp_server/register.py +415 -0
  500. ksadk/skills/mcp_server/server.py +218 -0
  501. ksadk/skills/models.py +37 -4
  502. ksadk/skills/observability.py +132 -0
  503. ksadk/skills/package_store.py +261 -40
  504. ksadk/skills/runtime/agent.py +67 -9
  505. ksadk/skills/runtime/artifact_delivery.py +139 -0
  506. ksadk/skills/runtime/artifacts.py +87 -0
  507. ksadk/skills/runtime/backends/e2b.py +460 -27
  508. ksadk/skills/runtime/backends/local.py +629 -62
  509. ksadk/skills/runtime/base.py +81 -7
  510. ksadk/skills/runtime/executor.py +268 -1
  511. ksadk/skills/runtime/loader.py +118 -5
  512. ksadk/skills/runtime/pinned.py +114 -0
  513. ksadk/skills/runtime/request.py +49 -0
  514. ksadk/skills/service_client.py +167 -28
  515. ksadk/skills/tool_defs.py +108 -3
  516. ksadk/studio/api.py +810 -93
  517. ksadk/studio/api_catalog_routes.py +18 -0
  518. ksadk/studio/api_contracts.py +8 -2
  519. ksadk/studio/api_plugin_routes.py +562 -104
  520. ksadk/studio/api_resource_connections.py +221 -0
  521. ksadk/studio/authoring.py +12 -6
  522. ksadk/studio/authoring_coordinator.py +23 -16
  523. ksadk/studio/builder.py +88 -0
  524. ksadk/studio/capabilities.py +23 -6
  525. ksadk/studio/channel_connections.py +333 -0
  526. ksadk/studio/cloud.py +370 -112
  527. ksadk/studio/cloud_shared_web.py +638 -0
  528. ksadk/studio/codex_agent_service.py +130 -28
  529. ksadk/studio/codex_builder.py +242 -3
  530. ksadk/studio/codex_credentials.py +90 -0
  531. ksadk/studio/codex_manifest.py +31 -4
  532. ksadk/studio/codex_plugin_store.py +746 -0
  533. ksadk/studio/codex_provider_build.py +231 -0
  534. ksadk/studio/codex_run.py +190 -26
  535. ksadk/studio/compatibility_report.py +12 -1
  536. ksadk/studio/compiler.py +88 -14
  537. ksadk/studio/configuration.py +182 -0
  538. ksadk/studio/contracts.py +165 -11
  539. ksadk/studio/dsh_application.py +247 -0
  540. ksadk/studio/dsh_capability_service.py +1374 -0
  541. ksadk/studio/dsh_lifecycle.py +58 -0
  542. ksadk/studio/dsh_models.py +59 -0
  543. ksadk/studio/dsh_provider_registration.py +367 -21
  544. ksadk/studio/entry.py +53 -0
  545. ksadk/studio/event_store.py +36 -5
  546. ksadk/studio/execution_host.py +525 -0
  547. ksadk/studio/framework_run.py +32 -10
  548. ksadk/studio/hosted_kernel.py +89 -14
  549. ksadk/studio/kernel_registry.py +418 -0
  550. ksadk/studio/kernel_sqlite_migration.py +181 -0
  551. ksadk/studio/mcp_runtime.py +11 -3
  552. ksadk/studio/model_client.py +1180 -66
  553. ksadk/studio/model_profile_service.py +26 -11
  554. ksadk/studio/native_documents.py +172 -0
  555. ksadk/studio/plugin_composition.py +202 -24
  556. ksadk/studio/plugin_kernel_adapter.py +21 -106
  557. ksadk/studio/plugin_lifecycle.py +80 -0
  558. ksadk/studio/plugin_runtime.py +448 -56
  559. ksadk/studio/provider_recovery.py +59 -0
  560. ksadk/studio/recovery_page.py +70 -0
  561. ksadk/studio/resource_authority.py +664 -0
  562. ksadk/studio/resource_binding_validation.py +77 -0
  563. ksadk/studio/resource_build_admission.py +196 -0
  564. ksadk/studio/resource_build_materializer.py +149 -0
  565. ksadk/studio/resource_catalog.py +149 -2
  566. ksadk/studio/resource_connections.py +220 -0
  567. ksadk/studio/run_activity.py +895 -0
  568. ksadk/studio/run_documents.py +127 -0
  569. ksadk/studio/run_service.py +789 -83
  570. ksadk/studio/runtime_catalog.py +53 -10
  571. ksadk/studio/runtime_source.py +16 -8
  572. ksadk/studio/scheduler_assistant.py +317 -0
  573. ksadk/studio/scheduler_runtime.py +32 -280
  574. ksadk/studio/scheduler_service.py +3 -0
  575. ksadk/studio/service.py +861 -120
  576. ksadk/studio/shared_web.py +579 -82
  577. ksadk/studio/static/assets/ChannelsPage-D_PGmKVq.js +8 -0
  578. ksadk/studio/static/assets/CodeBlock-DMIf4h33-UIqo-BPW.js +44 -0
  579. ksadk/studio/static/assets/ExecutionTree-v3HexvPo-Bk8wzGSy.js +1 -0
  580. ksadk/studio/static/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
  581. ksadk/studio/static/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
  582. ksadk/studio/static/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
  583. ksadk/studio/static/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
  584. ksadk/studio/static/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
  585. ksadk/studio/static/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
  586. ksadk/studio/static/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
  587. ksadk/studio/static/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
  588. ksadk/studio/static/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
  589. ksadk/studio/static/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
  590. ksadk/studio/static/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
  591. ksadk/studio/static/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
  592. ksadk/studio/static/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
  593. ksadk/studio/static/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
  594. ksadk/studio/static/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
  595. ksadk/studio/static/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
  596. ksadk/studio/static/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
  597. ksadk/studio/static/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
  598. ksadk/studio/static/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
  599. ksadk/studio/static/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
  600. ksadk/studio/static/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
  601. ksadk/studio/static/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
  602. ksadk/studio/static/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
  603. ksadk/studio/static/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
  604. ksadk/studio/static/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
  605. ksadk/studio/static/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
  606. ksadk/studio/static/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
  607. ksadk/studio/static/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
  608. ksadk/studio/static/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
  609. ksadk/studio/static/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
  610. ksadk/studio/static/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
  611. ksadk/studio/static/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
  612. ksadk/studio/static/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
  613. ksadk/studio/static/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
  614. ksadk/studio/static/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
  615. ksadk/studio/static/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
  616. ksadk/studio/static/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
  617. ksadk/studio/static/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
  618. ksadk/studio/static/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
  619. ksadk/studio/static/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
  620. ksadk/studio/static/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
  621. ksadk/studio/static/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
  622. ksadk/studio/static/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
  623. ksadk/studio/static/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
  624. ksadk/studio/static/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
  625. ksadk/studio/static/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
  626. ksadk/studio/static/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
  627. ksadk/studio/static/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
  628. ksadk/studio/static/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
  629. ksadk/studio/static/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
  630. ksadk/studio/static/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
  631. ksadk/studio/static/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
  632. ksadk/studio/static/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
  633. ksadk/studio/static/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
  634. ksadk/studio/static/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
  635. ksadk/studio/static/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
  636. ksadk/studio/static/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
  637. ksadk/studio/static/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
  638. ksadk/studio/static/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
  639. ksadk/studio/static/assets/MathMessageMarkdown-BesSeCYl-BEOyPC3w.css +1 -0
  640. ksadk/studio/static/assets/MathMessageMarkdown-BesSeCYl-OhNwH4K9.js +8 -0
  641. ksadk/studio/static/assets/MermaidBlock-H_0qzQDf-BdXtxug_.js +11 -0
  642. ksadk/studio/static/assets/MessageMarkdown-CwMTvgFj-g0I-I4dD.js +59 -0
  643. ksadk/studio/static/assets/PrismRenderer-5pK6tUdb.js +3 -0
  644. ksadk/studio/static/assets/TeamsPage-DqX4HWKQ.js +3 -0
  645. ksadk/studio/static/assets/abnfDiagram-VCTEODGH-Bnb1O3tA.js +1 -0
  646. ksadk/studio/static/assets/arc-Pry-7G1_.js +1 -0
  647. ksadk/studio/static/assets/architecture-7GRP2DOG-BMOPAkUC.js +1 -0
  648. ksadk/studio/static/assets/architectureDiagram-5GKGNRK7-5Zn1gqJv.js +36 -0
  649. ksadk/studio/static/assets/array-BifhSqXX.js +1 -0
  650. ksadk/studio/static/assets/blockDiagram-I7D4REHJ-CLB4L11u.js +129 -0
  651. ksadk/studio/static/assets/c4Diagram-7LVT6UL2-BOma_Z_F.js +38 -0
  652. ksadk/studio/static/assets/channel-r8itPEik.js +1 -0
  653. ksadk/studio/static/assets/chunk-2E4U76K2-BFf_huql.js +2 -0
  654. ksadk/studio/static/assets/chunk-2Q5K7J3B-C1jixKkw.js +1 -0
  655. ksadk/studio/static/assets/chunk-2ZTRR5NV-C2qZJila.js +2 -0
  656. ksadk/studio/static/assets/chunk-3Z5EZCMW-DMniOlPd.js +1 -0
  657. ksadk/studio/static/assets/chunk-4HAMMTFA-DVtDP4rm.js +62 -0
  658. ksadk/studio/static/assets/chunk-5V3GS4D5-i-ZDOEnP.js +1 -0
  659. ksadk/studio/static/assets/chunk-5VM5RSS4-ZNzvKenW.js +15 -0
  660. ksadk/studio/static/assets/chunk-6AZGARVD-BB6FqMl-.js +1 -0
  661. ksadk/studio/static/assets/chunk-6EIED4P4-DdUJkP0f.js +1 -0
  662. ksadk/studio/static/assets/chunk-6K3QC6MW-CqVsVn-y.js +1 -0
  663. ksadk/studio/static/assets/chunk-6TQVIW2G-DKlLbNIP.js +1 -0
  664. ksadk/studio/static/assets/chunk-747NJXEK-BFkZPlab.js +2 -0
  665. ksadk/studio/static/assets/chunk-75Z2AOVW-BZNhM4vB.js +2 -0
  666. ksadk/studio/static/assets/chunk-CLGD4ZFX-D-GTtyKs.js +1 -0
  667. ksadk/studio/static/assets/chunk-DU6HZSFF-CpEZCUgg.js +128 -0
  668. ksadk/studio/static/assets/chunk-F27PBJKO-D7pvKf0C.js +1 -0
  669. ksadk/studio/static/assets/chunk-FOHPRMQF-DHwB1DNv.js +161 -0
  670. ksadk/studio/static/assets/chunk-GMAD6QVW-DE_8H717.js +72 -0
  671. ksadk/studio/static/assets/chunk-GVQU2GXP-BfEB0lNv.js +1 -0
  672. ksadk/studio/static/assets/chunk-I5DQTOEV-Hm2LfiYI.js +1 -0
  673. ksadk/studio/static/assets/chunk-ICYGCRZG-749o-pGJ.js +1 -0
  674. ksadk/studio/static/assets/chunk-IH6LHLGP-CC6I9F3i.js +1 -0
  675. ksadk/studio/static/assets/chunk-IMKFNOWR-D38axYOR.js +231 -0
  676. ksadk/studio/static/assets/chunk-JWPE2WC7-DVXcaiue.js +1 -0
  677. ksadk/studio/static/assets/chunk-KI3K4JFJ-Rma-0k_M.js +1 -0
  678. ksadk/studio/static/assets/chunk-L3NEJ4N5-Dq5xn7aL.js +1 -0
  679. ksadk/studio/static/assets/chunk-LNGE3PJU-DWpGwHrW.js +34 -0
  680. ksadk/studio/static/assets/chunk-OSK3NFVY-DiCQjx9b.js +10 -0
  681. ksadk/studio/static/assets/chunk-OUJLGHUK-D0j2JRSg.js +2 -0
  682. ksadk/studio/static/assets/chunk-P2QGCYS3-VdhKmfYq.js +1 -0
  683. ksadk/studio/static/assets/chunk-POPQ4Y6H-C1_rzO5M.js +1 -0
  684. ksadk/studio/static/assets/chunk-PWAF6VOD-B45HvflI.js +1 -0
  685. ksadk/studio/static/assets/chunk-SHT3W25Y-DsCFnRVE.js +168 -0
  686. ksadk/studio/static/assets/chunk-SVP7TREG-DljApgRX.js +88 -0
  687. ksadk/studio/static/assets/chunk-TICWLB2K-DWqRtyqa.js +206 -0
  688. ksadk/studio/static/assets/chunk-TLUHSLCS-DUeGgnD5.js +2 -0
  689. ksadk/studio/static/assets/chunk-UY3FDG6J-CnmaAkG5.js +1 -0
  690. ksadk/studio/static/assets/chunk-XHIXRSVI-BFQ6B8BE.js +1 -0
  691. ksadk/studio/static/assets/chunk-XXDRQBXY-DqAYeljN.js +1 -0
  692. ksadk/studio/static/assets/chunk-Y2CYZVJY-DsF7k-Jl.js +1 -0
  693. ksadk/studio/static/assets/classDiagram-ZZMXUADV-DjeMNJmu.js +1 -0
  694. ksadk/studio/static/assets/classDiagram-v2-VYDZK3BY-DjeMNJmu.js +1 -0
  695. ksadk/studio/static/assets/clsx-DB0hHKMi.js +1 -0
  696. ksadk/studio/static/assets/cose-bilkent-JH36ORCC-om8AY0Mc.js +1 -0
  697. ksadk/studio/static/assets/createLucideIcon-DR0LGTP5.js +1 -0
  698. ksadk/studio/static/assets/cynefin-OW5HDTMX-7tP7QfM3.js +1 -0
  699. ksadk/studio/static/assets/cynefinDiagram-5FMLGOSQ-6BrmK52S.js +62 -0
  700. ksadk/studio/static/assets/cytoscape.esm-CGd-uY3x.js +321 -0
  701. ksadk/studio/static/assets/dagre-GXQ25YYZ-buZBRlwz.js +4 -0
  702. ksadk/studio/static/assets/dagre-PrKaheQc.js +1 -0
  703. ksadk/studio/static/assets/defaultLocale-BFoDCU3G.js +1 -0
  704. ksadk/studio/static/assets/diagram-S7CK7UJ4-C6aH78c4.js +30 -0
  705. ksadk/studio/static/assets/diagram-UQ7AKVKN-DY_tQ6C_.js +41 -0
  706. ksadk/studio/static/assets/diagram-VSXAHHWV-DX9p5cQT.js +3 -0
  707. ksadk/studio/static/assets/diagram-VX7I27RA-DB2WSH8X.js +24 -0
  708. ksadk/studio/static/assets/diagram-Z3DM3KII-Ce1BJwHE.js +24 -0
  709. ksadk/studio/static/assets/dist-v5Q1xZ2K.js +1 -0
  710. ksadk/studio/static/assets/ebnfDiagram-PWID7BFC-Daukxwqf.js +1 -0
  711. ksadk/studio/static/assets/erDiagram-RLTQ6QDP-CGthTMmJ.js +99 -0
  712. ksadk/studio/static/assets/eventmodeling-NTZA5JFV-BR2FzK4R.js +1 -0
  713. ksadk/studio/static/assets/flowDiagram-HODETNUW-CZ1tEenp.js +1 -0
  714. ksadk/studio/static/assets/ganttDiagram-EL5Y4UJY-BfoAZW9X.js +292 -0
  715. ksadk/studio/static/assets/gitGraph-4MIJSDKK-DYlXv_yJ.js +1 -0
  716. ksadk/studio/static/assets/gitGraphDiagram-WWUBYQGX-S985OIJF.js +106 -0
  717. ksadk/studio/static/assets/graphlib-DS17s2tU.js +1 -0
  718. ksadk/studio/static/assets/iconfont-4HN3C9Pg.woff2 +0 -0
  719. ksadk/studio/static/assets/index-CfEnms6Y.css +2 -0
  720. ksadk/studio/static/assets/index-Dp61rOWZ.js +0 -0
  721. ksadk/studio/static/assets/index.dom-BQenBtFm.js +1 -0
  722. ksadk/studio/static/assets/info-A6RAGUB7-BlU-8_xu.js +1 -0
  723. ksadk/studio/static/assets/infoDiagram-27XIBGKW-CO3W8tFn.js +2 -0
  724. ksadk/studio/static/assets/init-C-OQMol4.js +1 -0
  725. ksadk/studio/static/assets/ishikawaDiagram-5VMMS53U-BUGEl8cO.js +70 -0
  726. ksadk/studio/static/assets/journeyDiagram-3NMN7TZE-Ch2ZYxvr.js +139 -0
  727. ksadk/studio/static/assets/kanban-definition-UXKFOSKX-Da-Fp4vC.js +89 -0
  728. ksadk/studio/static/assets/katex-DhON-x74.js +257 -0
  729. ksadk/studio/static/assets/kingsoft-cloud-BOcLiQ3e.ico +0 -0
  730. ksadk/studio/static/assets/line-BDaeq300.js +1 -0
  731. ksadk/studio/static/assets/linear-DmucvK78.js +1 -0
  732. ksadk/studio/static/assets/mermaid-parser.core-Cj5mYdBi.js +4 -0
  733. ksadk/studio/static/assets/mindmap-definition-YA3MSWOX-CvlsVQ38.js +96 -0
  734. ksadk/studio/static/assets/ordinal-BDEzSJ7C.js +1 -0
  735. ksadk/studio/static/assets/packet-AYTQ26CC-BSJEH9Lb.js +1 -0
  736. ksadk/studio/static/assets/path-fybaL0A-.js +1 -0
  737. ksadk/studio/static/assets/pegDiagram-XKGWAZYB-BuHuNgT5.js +1 -0
  738. ksadk/studio/static/assets/pie-WAS4IAKB-qxv01Y8W.js +1 -0
  739. ksadk/studio/static/assets/pieDiagram-E7YTZNPT-DlrWiSOv.js +39 -0
  740. ksadk/studio/static/assets/preload-helper-CO68xz6u.js +1 -0
  741. ksadk/studio/static/assets/presentation-yYw7xK60-BmcZriri.js +1 -0
  742. ksadk/studio/static/assets/quadrantDiagram-AXDQQJYC-BA3hpUZM.js +7 -0
  743. ksadk/studio/static/assets/radar-RG4KPBEZ-B6h6pWOs.js +1 -0
  744. ksadk/studio/static/assets/railroad-74A4TZTK-CGtTWdGc.js +1 -0
  745. ksadk/studio/static/assets/railroad-abnf-HS5TGJTU-Cq7y22IA.js +1 -0
  746. ksadk/studio/static/assets/railroad-ebnf-LZEXJU2U-V0aQVv9m.js +1 -0
  747. ksadk/studio/static/assets/railroad-peg-WCYAUIDC-BQiIsX_p.js +1 -0
  748. ksadk/studio/static/assets/railroadDiagram-O6MQD6OU-BxpAazym.js +1 -0
  749. ksadk/studio/static/assets/requirementDiagram-BXWQKSXE-E-Ge0FcV.js +84 -0
  750. ksadk/studio/static/assets/rolldown-runtime-Dy4uBu1J-DK3Fl9T5.js +1 -0
  751. ksadk/studio/static/assets/rough.esm-Dy-Kn_BL.js +1 -0
  752. ksadk/studio/static/assets/sankeyDiagram-P5KCCOFB-CA6YKhQ3.js +40 -0
  753. ksadk/studio/static/assets/sequenceDiagram-WJ2MYXX4-CcElXGg4.js +162 -0
  754. ksadk/studio/static/assets/sizeCapture-INFHLROL-B0uUizjq.js +1 -0
  755. ksadk/studio/static/assets/src-B6xuSHsQ.js +1 -0
  756. ksadk/studio/static/assets/stateDiagram-D77RDMKH-DdXZOFqY.js +1 -0
  757. ksadk/studio/static/assets/stateDiagram-v2-MP3YSRHH-CJbOM-QK.js +1 -0
  758. ksadk/studio/static/assets/swimlanes-42K2YHIH-NtgEEHLY.js +1 -0
  759. ksadk/studio/static/assets/swimlanesDiagram-VR7AAH4N-D-AVlW8B.js +8 -0
  760. ksadk/studio/static/assets/timeline-definition-24CTP7MA-CIuYhNy3.js +120 -0
  761. ksadk/studio/static/assets/treeView-Q6P3EWNA-BTTYcvrF.js +1 -0
  762. ksadk/studio/static/assets/treemap-WGGIJYW6-Cg7tyfH8.js +1 -0
  763. ksadk/studio/static/assets/vennDiagram-4TSXK5OY-7HbApbrv.js +34 -0
  764. ksadk/studio/static/assets/wardley-WFR3VGLG-C6N1rULY.js +1 -0
  765. ksadk/studio/static/assets/wardleyDiagram-VM6X3IG4-CMJiaedy.js +78 -0
  766. ksadk/studio/static/assets/xychartDiagram-S5SC5T6Z-BdgtMvW2.js +7 -0
  767. ksadk/studio/static/index.html +8 -2
  768. ksadk/studio/teams_catalog.py +133 -0
  769. ksadk/studio/teams_installation.py +214 -0
  770. ksadk/studio/teams_node.py +545 -0
  771. ksadk/studio/teams_node_runner.py +69 -0
  772. ksadk/studio/teams_remote.py +274 -0
  773. ksadk/studio/templates.py +40 -19
  774. ksadk/studio/validator.py +13 -0
  775. ksadk/studio/workspace.py +19 -4
  776. ksadk/studio/workspace_plugins.py +136 -0
  777. ksadk/studio/workspace_registry.py +286 -0
  778. ksadk/toolsets/__init__.py +11 -0
  779. ksadk/toolsets/skills.py +446 -378
  780. ksadk/toolsets/workspace.py +3 -3
  781. ksadk/toolsets/workspace_identity.py +15 -0
  782. ksadk/version.py +1 -1
  783. ksadk_runtime_common/memory_backend/providers/lancedb.py +56 -2
  784. ksadk_runtime_common/schemas/memory_backend_manifest.schema.json +148 -148
  785. ksadk_runtime_common/workspace_files/router.py +26 -13
  786. agentengine_sdk_python-0.8.3.dist-info/RECORD +0 -833
  787. ksadk/server/static/assets/MathMessageMarkdown-D9uB55Oh.js +0 -8
  788. ksadk/server/static/assets/architecture-7GRP2DOG-Dk9nRQ6-.js +0 -1
  789. ksadk/server/static/assets/channel-s354Yo6o.js +0 -1
  790. ksadk/server/static/assets/chunk-2Q5K7J3B-CDpPpKR5.js +0 -1
  791. ksadk/server/static/assets/classDiagram-DTDB5LWJ-Dd0nucl9.js +0 -1
  792. ksadk/server/static/assets/classDiagram-v2-JRS7N3AN-Dd0nucl9.js +0 -1
  793. ksadk/server/static/assets/cynefin-OW5HDTMX-BBlT4uMf.js +0 -1
  794. ksadk/server/static/assets/dist-DdRBIceA.js +0 -1
  795. ksadk/server/static/assets/eventmodeling-NTZA5JFV-Dr5okd8V.js +0 -1
  796. ksadk/server/static/assets/flowDiagram-A5DVABFB-VEO-cVcO.js +0 -1
  797. ksadk/server/static/assets/gitGraph-4MIJSDKK-CKiKY9Cf.js +0 -1
  798. ksadk/server/static/assets/index-B2k_urY8.js +0 -241
  799. ksadk/server/static/assets/index-ILPt6IWF.css +0 -2
  800. ksadk/server/static/assets/info-A6RAGUB7-ubA4Nm_p.js +0 -1
  801. ksadk/server/static/assets/packet-AYTQ26CC-C7R3NUlK.js +0 -1
  802. ksadk/server/static/assets/pie-WAS4IAKB-BIT6qjr3.js +0 -1
  803. ksadk/server/static/assets/radar-RG4KPBEZ-l5noef9X.js +0 -1
  804. ksadk/server/static/assets/railroad-74A4TZTK-Baea6ZvM.js +0 -1
  805. ksadk/server/static/assets/railroad-abnf-HS5TGJTU-OKn8zZnU.js +0 -1
  806. ksadk/server/static/assets/railroad-ebnf-LZEXJU2U-DdKAKC4l.js +0 -1
  807. ksadk/server/static/assets/railroad-peg-WCYAUIDC-Bds8udlG.js +0 -1
  808. ksadk/server/static/assets/stateDiagram-v2-4QOOHH4V-5b-8o2iS.js +0 -1
  809. ksadk/server/static/assets/swimlanesDiagram-VK2B7HYN-uf6WPRA4.js +0 -8
  810. ksadk/server/static/assets/treeView-Q6P3EWNA-CK5CSrSO.js +0 -1
  811. ksadk/server/static/assets/treemap-WGGIJYW6-Ch2BKgAM.js +0 -1
  812. ksadk/server/static/assets/wardley-WFR3VGLG-YW3Q0ml_.js +0 -1
  813. ksadk/studio/static/assets/PrismRenderer-IYN-ffww.js +0 -3
  814. ksadk/studio/static/assets/index-DXs1Eq4M.css +0 -1
  815. ksadk/studio/static/assets/index-DeDqwTsD.js +0 -283
  816. {agentengine_sdk_python-0.8.3.dist-info → agentengine_sdk_python-0.8.5.dist-info}/WHEEL +0 -0
  817. {agentengine_sdk_python-0.8.3.dist-info → agentengine_sdk_python-0.8.5.dist-info}/entry_points.txt +0 -0
  818. {agentengine_sdk_python-0.8.3.dist-info → agentengine_sdk_python-0.8.5.dist-info}/licenses/LICENSE +0 -0
  819. {agentengine_sdk_python-0.8.3.dist-info → agentengine_sdk_python-0.8.5.dist-info}/top_level.txt +0 -0
ksadk/studio/api.py CHANGED
@@ -4,19 +4,24 @@ from __future__ import annotations
4
4
 
5
5
  import asyncio
6
6
  import base64
7
+ import hashlib
7
8
  import hmac
8
9
  import json
10
+ import logging
9
11
  import os
10
12
  import secrets
11
- import time
13
+ import shutil
14
+ import subprocess
15
+ import sys
12
16
  from collections.abc import AsyncIterator
13
17
  from contextlib import asynccontextmanager
14
18
  from pathlib import Path
15
19
  from typing import Any, Literal
20
+ from urllib.parse import quote
16
21
 
17
22
  from fastapi import FastAPI, File, Header, Query, Request, Response, UploadFile
18
23
  from fastapi.exceptions import RequestValidationError
19
- from fastapi.responses import FileResponse, JSONResponse, StreamingResponse
24
+ from fastapi.responses import FileResponse, JSONResponse, RedirectResponse, StreamingResponse
20
25
  from fastapi.staticfiles import StaticFiles
21
26
  from starlette.background import BackgroundTask
22
27
 
@@ -43,6 +48,7 @@ from ksadk.studio.api_contracts import (
43
48
  CreateAgentRequest,
44
49
  ImportRootRequest,
45
50
  InteractionSubmitRequest,
51
+ LinkedDirectoryRequest,
46
52
  ProjectInspectRequest,
47
53
  PromptCompileRequest,
48
54
  QuickAuthoringRequest,
@@ -79,6 +85,13 @@ from ksadk.studio.api_helpers import (
79
85
  )
80
86
  from ksadk.studio.api_memory_routes import register_memory_routes
81
87
  from ksadk.studio.api_plugin_routes import register_plugin_routes
88
+ from ksadk.studio.api_resource_connections import register_resource_connection_routes
89
+ from ksadk.studio.channel_connections import (
90
+ CHANNEL_ACTIONS,
91
+ ChannelAgentConnectionInput,
92
+ ChannelConnectionSettings,
93
+ )
94
+ from ksadk.studio.cloud_shared_web import CloudSharedWebBridge, cloud_chat_target, is_cloud_agent_id
82
95
  from ksadk.studio.codex_manifest import CodexAgentManifest
83
96
  from ksadk.studio.contracts import (
84
97
  AgentAppearance,
@@ -89,8 +102,23 @@ from ksadk.studio.contracts import (
89
102
  OperationStatus,
90
103
  )
91
104
  from ksadk.studio.errors import StudioError
105
+ from ksadk.studio.native_documents import (
106
+ NativeDocumentActionRequest,
107
+ document_metadata,
108
+ is_local_desktop_request,
109
+ perform_document_action,
110
+ )
111
+ from ksadk.studio.run_activity import register_activity_routes
112
+ from ksadk.studio.run_documents import read_document
92
113
  from ksadk.studio.service import StudioService
93
114
  from ksadk.studio.shared_web import StudioSharedWebBridge
115
+ from ksadk.studio.workspace_registry import (
116
+ LinkedDirectoryPolicy,
117
+ WorkspaceRegistry,
118
+ WorkspaceRuntimeManager,
119
+ )
120
+
121
+ logger = logging.getLogger(__name__)
94
122
 
95
123
  _WRITE_METHODS = {"POST", "PUT", "PATCH", "DELETE"}
96
124
  _PUBLIC_API_PATHS = {
@@ -144,9 +172,7 @@ _CONTENT_WIRE_ALIASES = {
144
172
  def _normalize_cloud_runtime_event_wire(value: dict[str, Any]) -> dict[str, Any]:
145
173
  """Normalize only historic REST casing at the RuntimeEvent boundary."""
146
174
 
147
- normalized = {
148
- _RUNTIME_EVENT_WIRE_ALIASES.get(key, key): item for key, item in value.items()
149
- }
175
+ normalized = {_RUNTIME_EVENT_WIRE_ALIASES.get(key, key): item for key, item in value.items()}
150
176
  source = normalized.get("source")
151
177
  if isinstance(source, dict):
152
178
  normalized["source"] = {
@@ -198,9 +224,7 @@ def _cloud_event_conversation_item(
198
224
  if raw_event is None:
199
225
  return None
200
226
  try:
201
- runtime_event = parse_runtime_event_lenient(
202
- _normalize_cloud_runtime_event_wire(raw_event)
203
- )
227
+ runtime_event = parse_runtime_event_lenient(_normalize_cloud_runtime_event_wire(raw_event))
204
228
  item = project_conversation_item(
205
229
  runtime_event, # type: ignore[arg-type]
206
230
  session_id=session_id,
@@ -211,6 +235,21 @@ def _cloud_event_conversation_item(
211
235
  return item.model_dump(by_alias=True, exclude_none=True, mode="json")
212
236
 
213
237
 
238
+ import re as _re
239
+
240
+ # 内容 hash 文件名(如 index-CQj_Jm9z.js)可安全 immutable 强缓存;
241
+ # index.html 与动态 HTML 仍需每次校验。
242
+ _HASHED_ASSET_RE = _re.compile(r"-[A-Za-z0-9_]{8,}\.(js|css|woff2?|ttf|png|svg|webp|mp4|wasm)$")
243
+
244
+
245
+ def _cache_control_for(path: str, response: Any) -> str:
246
+ if path.startswith(("/api/", "/v1/")):
247
+ return "no-store"
248
+ if _HASHED_ASSET_RE.search(path) and 200 <= response.status_code < 300:
249
+ return "public, max-age=31536000, immutable"
250
+ return response.headers.get("Cache-Control", "no-cache")
251
+
252
+
214
253
  def create_studio_app(
215
254
  root: Path | str,
216
255
  *,
@@ -219,21 +258,114 @@ def create_studio_app(
219
258
  csrf_token: str | None = None,
220
259
  security_enabled: bool = True,
221
260
  ) -> FastAPI:
222
- studio = service or StudioService(root)
261
+ initial_service = service or StudioService(root)
262
+ studio = WorkspaceRuntimeManager(
263
+ initial_service, lambda workspace_root: StudioService(workspace_root)
264
+ )
223
265
  session_secret = session_token or secrets.token_urlsafe(32)
224
266
  csrf_secret = csrf_token or secrets.token_urlsafe(24)
267
+ session_cookie_name = "agentkit_studio_session_" + hashlib.sha256(
268
+ session_secret.encode("utf-8")
269
+ ).hexdigest()[:16]
270
+ legacy_session_cookie_name = "agentkit_studio_session"
225
271
 
226
272
  @asynccontextmanager
227
273
  async def lifespan(_app: FastAPI) -> AsyncIterator[None]:
274
+ lazy_desktop_start = os.environ.get("KSADK_STUDIO_LAZY_START") == "1"
275
+ warmups: set[asyncio.Task[None]] = set()
276
+
277
+ def schedule_runtime_warmup(runtime: StudioService) -> None:
278
+ async def warm_runtime() -> None:
279
+ try:
280
+ await runtime.start()
281
+ except Exception:
282
+ # Runtime warmup is best effort; core Studio remains usable
283
+ # while optional DSH capability discovery is unavailable.
284
+ return
285
+ # The Channel UI is a wheel-owned client bundle. Bootstrap it
286
+ # after the listener is available and through the same Profile
287
+ # maintenance fence used by other DSH mutations.
288
+ if (
289
+ os.environ.get("KSADK_STUDIO_CHANNEL_DEFAULT", "1")
290
+ .strip()
291
+ .lower()
292
+ not in {"0", "false", "no", "off"}
293
+ and not os.environ.get("KSADK_DSH_HOME", "").strip()
294
+ and not os.environ.get("KSADK_DSH_PROFILE", "").strip()
295
+ ):
296
+ try:
297
+ from ksadk.plugins.dsh_channels import (
298
+ channel_default_bootstrap_needed,
299
+ configure_channel_profile,
300
+ )
301
+ from ksadk.studio.api_plugin_routes import _studio_dsh_options
302
+
303
+ home, profile, command, _ = _studio_dsh_options(runtime)
304
+ # 可选 bootstrap 必须先确认 DSH 命令真实可用:无工具链的
305
+ # 环境进入 Profile 维护栅栏后,恢复失败会按契约保持
306
+ # admission 关闭,导致整个会话面永久 503(回归过)。
307
+ if command and shutil.which(str(command[0])) is None:
308
+ logger.info(
309
+ "Channel 默认激活跳过:DSH 命令不可用 (%s)",
310
+ command[0],
311
+ )
312
+ elif profile == "web" and command and channel_default_bootstrap_needed(home):
313
+ async def configure_channel():
314
+ await asyncio.to_thread(
315
+ configure_channel_profile,
316
+ workspace=runtime.workspace.root,
317
+ dsh_home=home,
318
+ dsh_command=command,
319
+ )
320
+
321
+ await runtime.reconfigure_dsh_profile(configure_channel)
322
+ except Exception as error:
323
+ logger.info("Channel 默认激活未完成(可手动启用): %s", error)
324
+
325
+ if os.environ.get(
326
+ "KSADK_STUDIO_TEAMS_DEFAULT", "1"
327
+ ).strip().lower() not in {"0", "false", "no", "off"}:
328
+ installation = getattr(runtime, "teams_installation", None)
329
+ if installation is not None and not installation.status().get("enabled"):
330
+ try:
331
+ await installation.enable()
332
+ except Exception as error:
333
+ # 默认启用是预期行为,但 DSH 工具链/权威不可用时必须静默
334
+ # 降级,核心 Studio 与其他插件不受影响;页面仍保留手动入口。
335
+ logger.info(
336
+ "Teams 默认激活未完成(可手动启用): %s", error
337
+ )
338
+
339
+ task = asyncio.create_task(warm_runtime())
340
+ warmups.add(task)
341
+ task.add_done_callback(warmups.discard)
342
+
228
343
  try:
229
- await studio.start()
230
- await studio.run_service.recover_interrupted()
344
+ app.state.runtime_warmups = schedule_runtime_warmup
345
+ await studio.start(wait_for_dsh=not lazy_desktop_start)
346
+ # Optional plugin activation must not hold the HTTP listener closed
347
+ # while DSH starts subprocesses or acquires an authority lease.
348
+ schedule_runtime_warmup(studio.active)
349
+ # Pre-resolve the entry redirect choice once off the request path;
350
+ # failures are swallowed and ``index()`` has its own bounded wait.
351
+ probe_warmup = getattr(
352
+ studio.active.dsh_capabilities, "schedule_plugin_probe_warmup", None
353
+ )
354
+ if callable(probe_warmup):
355
+ probe_warmup()
356
+ await studio.run_service.recover_interrupted(studio.resolve_run_spec)
231
357
  await studio.scheduler.start_if_available()
232
358
  yield
233
359
  finally:
234
- await studio.scheduler.stop()
235
- await studio.aclose()
236
- studio.credentials.clear_session()
360
+ app.state.runtime_warmups = None
361
+ for task in tuple(warmups):
362
+ task.cancel()
363
+ if warmups:
364
+ await asyncio.gather(*warmups, return_exceptions=True)
365
+ for runtime in studio.services():
366
+ await runtime.scheduler.stop()
367
+ await runtime.aclose()
368
+ runtime.credentials.clear_session()
237
369
 
238
370
  app = FastAPI(
239
371
  title="AgentKit Local Studio",
@@ -246,6 +378,11 @@ def create_studio_app(
246
378
  app.state.studio_service = studio
247
379
  app.state.session_token = session_secret
248
380
  app.state.csrf_token = csrf_secret
381
+ from ksadk.studio.dsh_models import studio_model_projection
382
+
383
+ studio.dsh_capabilities.model_projection = lambda: studio_model_projection(
384
+ studio.catalog, studio.credentials
385
+ )
249
386
 
250
387
  def _stream_studio_run(
251
388
  build_id: str,
@@ -321,8 +458,9 @@ def create_studio_app(
321
458
  )
322
459
 
323
460
  static_root = Path(__file__).with_name("static")
324
- _studio_startup_epoch = str(int(time.time()))
325
461
  shared_web = StudioSharedWebBridge(studio)
462
+ register_activity_routes(app, studio, resolve_run_id=shared_web.resolve_run_reference)
463
+ cloud_web = CloudSharedWebBridge(studio)
326
464
  app.state.shared_web_bridge = shared_web
327
465
  app.mount("/static", StaticFiles(directory=static_root), name="studio-static")
328
466
 
@@ -377,8 +515,10 @@ def create_studio_app(
377
515
  or shared_web_api
378
516
  or responses_api
379
517
  ):
380
- supplied = request.cookies.get("agentkit_studio_session") or request.headers.get(
381
- "X-AgentKit-Session"
518
+ supplied = (
519
+ request.cookies.get(session_cookie_name)
520
+ or request.cookies.get(legacy_session_cookie_name)
521
+ or request.headers.get("X-AgentKit-Session")
382
522
  )
383
523
  if not supplied or not hmac.compare_digest(supplied, session_secret):
384
524
  return _error_response(
@@ -389,7 +529,8 @@ def create_studio_app(
389
529
  ),
390
530
  request,
391
531
  )
392
- if (studio_api or responses_api) and request.method in _WRITE_METHODS:
532
+ channel_api = shared_web_api and request.url.path.rsplit("/", 1)[-1] in CHANNEL_ACTIONS
533
+ if (studio_api or responses_api or channel_api) and request.method in _WRITE_METHODS:
393
534
  csrf = request.headers.get("X-CSRF-Token")
394
535
  if not csrf or not hmac.compare_digest(csrf, csrf_secret):
395
536
  return _error_response(
@@ -402,11 +543,7 @@ def create_studio_app(
402
543
  )
403
544
  response = await call_next(request)
404
545
  response.headers["X-Request-Id"] = request.state.request_id
405
- response.headers["Cache-Control"] = (
406
- "no-store"
407
- if request.url.path.startswith(("/api/", "/v1/"))
408
- else response.headers.get("Cache-Control", "no-cache")
409
- )
546
+ response.headers["Cache-Control"] = _cache_control_for(request.url.path, response)
410
547
  response.headers["X-Content-Type-Options"] = "nosniff"
411
548
  response.headers["X-Frame-Options"] = "DENY"
412
549
  response.headers["Referrer-Policy"] = "no-referrer"
@@ -464,29 +601,23 @@ def create_studio_app(
464
601
  )
465
602
 
466
603
  @app.get("/")
467
- async def index():
468
- path = static_root / "index.html"
469
- html = path.read_text(encoding="utf-8")
470
- # Inject a startup-scoped version so a restarted Studio with a rebuilt
471
- # bundle always wins over a stale browser tab. The bundle filenames
472
- # are already content-hashed; this only defeats cached index.html.
473
- if "?v=" not in html:
474
- import re
475
-
476
- def _add_version(match: "re.Match[str]") -> str:
477
- attr, path_part = match.group(1), match.group(2)
478
- return f'{attr}="/static/assets/{path_part}?v={_studio_startup_epoch}"'
479
-
480
- html = re.sub(
481
- r'(src|href)="/static/assets/([^"]+)"',
482
- _add_version,
483
- html,
484
- )
485
- response = Response(content=html, media_type="text/html")
486
- response.headers["Cache-Control"] = "no-store"
604
+ @app.get("/studio-recovery/")
605
+ @app.get("/studio-shell/")
606
+ async def index(request: Request):
607
+ from ksadk.studio.entry import studio_entry_response
608
+
609
+ response = await studio_entry_response(studio, request, static_root)
487
610
  if security_enabled:
488
611
  response.set_cookie(
489
- "agentkit_studio_session",
612
+ session_cookie_name,
613
+ session_secret,
614
+ httponly=True,
615
+ samesite="strict",
616
+ secure=False,
617
+ path="/",
618
+ )
619
+ response.set_cookie(
620
+ legacy_session_cookie_name,
490
621
  session_secret,
491
622
  httponly=True,
492
623
  samesite="strict",
@@ -495,6 +626,42 @@ def create_studio_app(
495
626
  )
496
627
  return response
497
628
 
629
+ @app.get("/api/v1/plugin-ecosystems/dsh/recovery")
630
+ async def dsh_recovery_status():
631
+ # This endpoint must remain a read-only snapshot: no Core startup, Profile
632
+ # projection, credential resolution, or filesystem repair is attempted.
633
+ return studio.dsh_capabilities.startup_status
634
+
635
+ @app.get("/api/v1/plugins/workspace-contributions")
636
+ async def workspace_contributions():
637
+ """Expose enabled UI contributions without booting the DSH Core."""
638
+ from ksadk.plugins.dsh_channels import channel_workspace_contributions
639
+ from ksadk.plugins.dsh_home import studio_dsh_home
640
+
641
+ runtime = studio.active
642
+ contributions = await asyncio.to_thread(
643
+ channel_workspace_contributions,
644
+ studio_dsh_home(runtime.workspace.root),
645
+ os.environ.get("KSADK_DSH_PROFILE", "").strip() or "web",
646
+ )
647
+ # Teams remains served by its existing lifecycle endpoint; this endpoint
648
+ # only adds client-only contributions that have no domain lifecycle.
649
+ return {"items": contributions}
650
+
651
+ @app.get("/api/v1/channels/connection")
652
+ async def channel_connection_status():
653
+ return studio.channel_connections.status()
654
+
655
+ @app.put("/api/v1/channels/connection")
656
+ async def configure_channel_connection(payload: ChannelConnectionSettings):
657
+ return await studio.channel_connections.configure(payload)
658
+
659
+ @app.put("/api/v1/channels/agents/{agent_id}/connection")
660
+ async def configure_channel_agent_connection(
661
+ agent_id: str, payload: ChannelAgentConnectionInput
662
+ ):
663
+ return await studio.channel_connections.configure_agent(agent_id, payload)
664
+
498
665
  @app.get("/favicon.ico")
499
666
  async def favicon():
500
667
  return Response(status_code=204)
@@ -526,9 +693,31 @@ def create_studio_app(
526
693
  async def openai_responses(payload: dict[str, Any]):
527
694
  metadata = payload.get("metadata")
528
695
  metadata = metadata if isinstance(metadata, dict) else {}
529
- agent_id = str(metadata.get("agent_id") or metadata.get("agentId") or "") or None
696
+ agentengine_metadata = metadata.get("agentengine")
697
+ agentengine_metadata = (
698
+ agentengine_metadata if isinstance(agentengine_metadata, dict) else {}
699
+ )
700
+ agent_id = (
701
+ str(
702
+ metadata.get("agent_id")
703
+ or metadata.get("agentId")
704
+ or agentengine_metadata.get("agent_id")
705
+ or agentengine_metadata.get("agentId")
706
+ or ""
707
+ )
708
+ or None
709
+ )
530
710
  requested_approval_mode = (
531
- str(metadata.get("approval_mode") or metadata.get("approvalMode") or "").strip().lower()
711
+ str(
712
+ metadata.get("approval_mode")
713
+ or metadata.get("approvalMode")
714
+ or agentengine_metadata.get("tool_approval_mode")
715
+ or agentengine_metadata.get("approval_mode")
716
+ or agentengine_metadata.get("approvalMode")
717
+ or ""
718
+ )
719
+ .strip()
720
+ .lower()
532
721
  )
533
722
  if requested_approval_mode and requested_approval_mode not in {"ask", "risk", "full"}:
534
723
  raise StudioError(
@@ -538,7 +727,13 @@ def create_studio_app(
538
727
  field="metadata.approval_mode",
539
728
  )
540
729
  collaboration_mode = (
541
- str(metadata.get("collaboration_mode") or metadata.get("collaborationMode") or "")
730
+ str(
731
+ metadata.get("collaboration_mode")
732
+ or metadata.get("collaborationMode")
733
+ or agentengine_metadata.get("collaboration_mode")
734
+ or agentengine_metadata.get("collaborationMode")
735
+ or ""
736
+ )
542
737
  .strip()
543
738
  .lower()
544
739
  )
@@ -550,7 +745,11 @@ def create_studio_app(
550
745
  field="metadata.collaboration_mode",
551
746
  )
552
747
  goal_objective = str(
553
- metadata.get("goal_objective") or metadata.get("goalObjective") or ""
748
+ metadata.get("goal_objective")
749
+ or metadata.get("goalObjective")
750
+ or agentengine_metadata.get("goal_objective")
751
+ or agentengine_metadata.get("goalObjective")
752
+ or ""
554
753
  ).strip()
555
754
  reasoning = payload.get("reasoning")
556
755
  reasoning = reasoning if isinstance(reasoning, dict) else {}
@@ -594,7 +793,7 @@ def create_studio_app(
594
793
 
595
794
  @app.post("/v1/responses/{response_id}/cancel")
596
795
  async def cancel_openai_response(response_id: str):
597
- result = shared_web.cancel_run(response_id)
796
+ result = await shared_web.cancel_run(response_id)
598
797
  return {
599
798
  "id": response_id,
600
799
  "object": "response",
@@ -618,26 +817,121 @@ def create_studio_app(
618
817
  try:
619
818
  cookie_agent_id = request.cookies.get("agentkit_studio_chat_agent")
620
819
  requested_agent_id = str(payload.get("AgentId") or cookie_agent_id or "")
820
+ cloud_mode = is_cloud_agent_id(requested_agent_id)
821
+ if cloud_mode:
822
+ # Cloud agents project onto the same shared-web action
823
+ # contract through the cloud-chat proxy, so the ksadk-web
824
+ # headless data layer drives them like hosted-ui does.
825
+ cloud_target = cloud_chat_target(requested_agent_id)
826
+ data = None
827
+ if action == "GetAgentUiBootstrap":
828
+ data = await cloud_web.bootstrap(cloud_target)
829
+ elif action == "ListAgentModels":
830
+ data = await cloud_web.list_models(cloud_target)
831
+ elif action == "ListSessions":
832
+ data = await cloud_web.list_sessions(
833
+ cloud_target,
834
+ page=int(payload.get("Page") or 1),
835
+ page_size=int(payload.get("PageSize") or 30),
836
+ )
837
+ elif action == "CreateSession":
838
+ data = await cloud_web.create_session(cloud_target)
839
+ elif action == "GetSession":
840
+ data = await cloud_web.get_session(
841
+ cloud_target, str(payload.get("SessionId") or "")
842
+ )
843
+ elif action == "DeleteSession":
844
+ data = await cloud_web.delete_session(
845
+ cloud_target, str(payload.get("SessionId") or "")
846
+ )
847
+ elif action == "ListSessionMessages":
848
+ data = await cloud_web.list_messages(
849
+ cloud_target,
850
+ str(payload.get("SessionId") or ""),
851
+ after_seq_id=_optional_int(payload.get("AfterSeqId")),
852
+ before_seq_id=_optional_int(payload.get("BeforeSeqId")),
853
+ limit=int(payload.get("Limit") or 50),
854
+ )
855
+ elif action == "ListSessionEvents":
856
+ data = await cloud_web.list_session_events(
857
+ cloud_target,
858
+ str(payload.get("SessionId") or ""),
859
+ after_seq_id=_optional_int(payload.get("AfterSeqId")),
860
+ offset=_optional_int(payload.get("Offset")),
861
+ limit=int(payload.get("Limit") or 200),
862
+ )
863
+ elif action == "SubmitInteraction":
864
+ data = await cloud_web.submit_interaction(
865
+ cloud_target, str(payload.get("SessionId") or ""), payload
866
+ )
867
+ elif action == "RunAgent":
868
+ # Open the upstream connection before StreamingResponse
869
+ # commits status 200, so admission failures remain a
870
+ # structured HTTP error instead of an empty SSE body.
871
+ upstream_stream = await cloud_web.open_run_stream(cloud_target, payload)
872
+ return StreamingResponse(
873
+ cloud_web.stream_run(
874
+ cloud_target,
875
+ payload,
876
+ upstream_stream=upstream_stream,
877
+ ),
878
+ media_type="text/event-stream",
879
+ headers={"Cache-Control": "no-store", "X-Accel-Buffering": "no"},
880
+ )
881
+ elif action in {
882
+ "GetResponseFeedback",
883
+ "UpsertResponseFeedback",
884
+ "DeleteResponseFeedback",
885
+ "ListSessionCheckpoints",
886
+ "ListToolReceipts",
887
+ }:
888
+ data = {"Feedback": None} if action == "GetResponseFeedback" else {}
889
+ else:
890
+ return JSONResponse(
891
+ status_code=404,
892
+ content={
893
+ "Code": 404,
894
+ "Message": f"Studio 尚未实现云端共享 Web 动作:{action}",
895
+ "Data": {},
896
+ },
897
+ )
898
+ return {"Code": 0, "Message": "OK", "Data": data}
621
899
  if action == "GetAgentUiBootstrap":
622
- data = shared_web.bootstrap(shared_web.resolve_agent_id(requested_agent_id or None))
623
- elif action == "ListAgentModels":
624
- data = shared_web.list_models(
625
- shared_web.resolve_agent_id(requested_agent_id or None)
900
+ bootstrap_agent_id = shared_web.resolve_agent_id(
901
+ requested_agent_id or None
626
902
  )
903
+ # 冷启动时 provider 模型描述符尚未发现;绑定 model:provider:*
904
+ # 的 Agent(尤其 Harness)若直接解析会 RESOURCE_NOT_FOUND,
905
+ # 前端只能无限转圈。访问资源库不应该是打开对话的前置条件。
906
+ if not bootstrap_agent_id.startswith(("ar-", "account:")):
907
+ await runtime_model_catalog()
908
+ data = shared_web.bootstrap(bootstrap_agent_id)
909
+ elif action == "ListAgentModels":
910
+ model_agent_id = shared_web.resolve_agent_id(requested_agent_id or None)
911
+ # A cold Studio process has no discovered provider descriptors.
912
+ # Resolve the same cached catalog as Resources before projecting
913
+ # model windows; visiting Resources must not be a prerequisite.
914
+ await runtime_model_catalog()
915
+ data = shared_web.list_models(model_agent_id)
627
916
  elif action == "ListSessions":
628
- data = shared_web.list_sessions(
917
+ data = await shared_web.list_sessions(
629
918
  shared_web.resolve_agent_id(requested_agent_id or None),
630
919
  page=int(payload.get("Page") or 1),
631
920
  page_size=int(payload.get("PageSize") or 30),
632
921
  )
633
922
  elif action == "CreateSession":
634
- data = shared_web.create_session(
923
+ data = await shared_web.create_session(
635
924
  shared_web.resolve_agent_id(requested_agent_id or None)
636
925
  )
637
926
  elif action == "GetSession":
638
- data = shared_web.get_session(str(payload.get("SessionId") or ""))
927
+ data = await shared_web.get_session(str(payload.get("SessionId") or ""))
928
+ elif action == "CompactSession":
929
+ data = await shared_web.compact_session(
930
+ shared_web.resolve_agent_id(requested_agent_id or None),
931
+ str(payload.get("SessionId") or ""),
932
+ )
639
933
  elif action == "DeleteSession":
640
- data = shared_web.delete_session(str(payload.get("SessionId") or ""))
934
+ data = await shared_web.delete_session(str(payload.get("SessionId") or ""))
641
935
  elif action == "ListSessionMessages":
642
936
  data = await shared_web.list_messages(
643
937
  str(payload.get("SessionId") or ""),
@@ -648,8 +942,12 @@ def create_studio_app(
648
942
  elif action == "ListSessionEvents":
649
943
  data = await shared_web.list_session_events(str(payload.get("SessionId") or ""))
650
944
  elif action == "RunAgent":
945
+ # A cold process has not discovered provider model descriptors
946
+ # yet. Run admission may need to materialize a current Build,
947
+ # so chat must not depend on visiting the Models page first.
948
+ await runtime_model_catalog()
651
949
  return StreamingResponse(
652
- shared_web.stream_run(payload),
950
+ shared_web.stream_run(payload, shared_ui=True),
653
951
  media_type="text/event-stream",
654
952
  headers={
655
953
  "Cache-Control": "no-store",
@@ -657,7 +955,35 @@ def create_studio_app(
657
955
  },
658
956
  )
659
957
  elif action == "CancelRun":
660
- data = shared_web.cancel_run(str(payload.get("InvocationId") or ""))
958
+ data = await shared_web.cancel_run(str(payload.get("InvocationId") or ""))
959
+ elif action == "SubmitInteraction":
960
+ run_id = str(payload.get("RunId") or "")
961
+ studio._require_direct_run(run_id)
962
+ interaction_id = str(payload.get("InteractionId") or "")
963
+ resolved = await studio.run_service.submit_interaction(
964
+ run_id,
965
+ interaction_id,
966
+ name=str(payload.get("Action") or "submit"),
967
+ data=(
968
+ dict(payload.get("Response"))
969
+ if isinstance(payload.get("Response"), dict)
970
+ else {}
971
+ ),
972
+ expected_revision=int(payload.get("ExpectedRevision") or 0),
973
+ idempotency_key=str(payload.get("IdempotencyKey") or ""),
974
+ )
975
+ data = {
976
+ "schema_version": 1,
977
+ "command_id": str(
978
+ resolved.get("eventId")
979
+ or resolved.get("resolutionEventId")
980
+ or f"interaction:{interaction_id}"
981
+ ),
982
+ "status": "accepted",
983
+ "message_id": None,
984
+ "run_id": run_id,
985
+ "accepted_seq": int(resolved.get("eventId") or 0),
986
+ }
661
987
  elif action in {
662
988
  "GetResponseFeedback",
663
989
  "UpsertResponseFeedback",
@@ -669,6 +995,10 @@ def create_studio_app(
669
995
  elif action == "ListToolReceipts":
670
996
  data = {"ToolReceipts": []}
671
997
  else:
998
+ # Channel-related actions: proxy to channel backend
999
+ if action in CHANNEL_ACTIONS:
1000
+ status, body = await studio.channel_connections.proxy(action, payload)
1001
+ return JSONResponse(status_code=status, content=body)
672
1002
  return JSONResponse(
673
1003
  status_code=404,
674
1004
  content={
@@ -689,12 +1019,37 @@ def create_studio_app(
689
1019
  )
690
1020
 
691
1021
  @app.get("/agentengine/api/v1/SubscribeRunEvents")
692
- async def shared_chat_subscribe_run_events():
693
- async def completed_stream():
694
- yield "event: done\ndata: [DONE]\n\n"
695
-
1022
+ async def shared_chat_subscribe_run_events(
1023
+ request: Request,
1024
+ session_id: str = Query(alias="SessionId"),
1025
+ invocation_id: str = Query(alias="InvocationId"),
1026
+ after_seq_id: int = Query(default=0, alias="AfterSeqId", ge=0),
1027
+ agent_id: str | None = Query(default=None, alias="AgentId"),
1028
+ ):
1029
+ requested_agent_id = str(
1030
+ agent_id or request.cookies.get("agentkit_studio_chat_agent") or ""
1031
+ )
1032
+ if is_cloud_agent_id(requested_agent_id):
1033
+ cloud_target = cloud_chat_target(requested_agent_id)
1034
+ return StreamingResponse(
1035
+ cloud_web.subscribe_run_events(
1036
+ cloud_target,
1037
+ session_id,
1038
+ invocation_id,
1039
+ after_seq_id=after_seq_id,
1040
+ ),
1041
+ media_type="text/event-stream",
1042
+ headers={"Cache-Control": "no-store", "X-Accel-Buffering": "no"},
1043
+ )
1044
+ # Validate before StreamingResponse sends headers; invalid identities
1045
+ # remain ordinary actionable HTTP errors.
1046
+ await shared_web.subscription_run_id(session_id, invocation_id)
696
1047
  return StreamingResponse(
697
- completed_stream(),
1048
+ shared_web.subscribe_run_events(
1049
+ session_id,
1050
+ invocation_id,
1051
+ after_seq_id=after_seq_id,
1052
+ ),
698
1053
  media_type="text/event-stream",
699
1054
  headers={"Cache-Control": "no-store", "X-Accel-Buffering": "no"},
700
1055
  )
@@ -716,7 +1071,15 @@ def create_studio_app(
716
1071
  status_code=401,
717
1072
  )
718
1073
  response.set_cookie(
719
- "agentkit_studio_session",
1074
+ session_cookie_name,
1075
+ session_secret,
1076
+ httponly=True,
1077
+ samesite="strict",
1078
+ secure=False,
1079
+ path="/",
1080
+ )
1081
+ response.set_cookie(
1082
+ legacy_session_cookie_name,
720
1083
  session_secret,
721
1084
  httponly=True,
722
1085
  samesite="strict",
@@ -735,13 +1098,15 @@ def create_studio_app(
735
1098
  "workspace": {
736
1099
  "name": studio.workspace.root.name,
737
1100
  "path": str(studio.workspace.root),
1101
+ "workspaceId": studio.workspace_record.workspace_id,
738
1102
  },
739
1103
  "operationScope": studio.deployment_operation_scope(),
1104
+ "frontend": studio.frontend_assets(),
740
1105
  "features": {
741
1106
  "build": True,
742
1107
  "run": True,
743
1108
  "runtimeRegistry": True,
744
- "runtimeTypes": ["codex", "adk", "langgraph"],
1109
+ "runtimeTypes": ["harness", "codex", "adk", "langgraph"],
745
1110
  "evaluation": True,
746
1111
  "deployment": True,
747
1112
  "cloudRebuild": False,
@@ -758,12 +1123,13 @@ def create_studio_app(
758
1123
 
759
1124
  @app.put("/api/v1/system/settings")
760
1125
  async def update_settings(payload: dict[str, Any]):
761
- return studio.update_settings(payload)
1126
+ return await studio.apply_settings(payload)
762
1127
 
763
1128
  @app.get("/api/v1/schedules")
764
1129
  async def list_schedules():
765
1130
  return {
766
1131
  "items": studio.scheduler.list_tasks(),
1132
+ "taskOccurrences": studio.scheduler.task_occurrence_summaries(),
767
1133
  "availability": studio.scheduler.availability(),
768
1134
  }
769
1135
 
@@ -835,6 +1201,10 @@ def create_studio_app(
835
1201
  task_id: str,
836
1202
  payload: AgentScheduleRequest,
837
1203
  ):
1204
+ existing = studio.get_agent_schedule(agent_id, task_id)
1205
+ await studio.validate_schedule_session(
1206
+ agent_id, payload.continuity, payload.session_id, existing.target.agent_version_ref
1207
+ )
838
1208
  return studio.update_agent_schedule(
839
1209
  agent_id,
840
1210
  task_id,
@@ -865,23 +1235,222 @@ def create_studio_app(
865
1235
  studio.get_agent_schedule(agent_id, task_id)
866
1236
  return {"items": studio.scheduler.list_occurrences(task_id, limit=limit)}
867
1237
 
1238
+ def _decode_text_file(path: Path) -> str:
1239
+ """Decode with an encoding fallback chain so legacy GBK/GB18030 files
1240
+ written by agents preview correctly instead of turning into mojibake."""
1241
+ raw = path.read_bytes()
1242
+ for encoding in ("utf-8-sig", "utf-8", "gb18030"):
1243
+ try:
1244
+ return raw.decode(encoding)
1245
+ except UnicodeDecodeError:
1246
+ continue
1247
+ return raw.decode("utf-8", errors="replace")
1248
+
1249
+ @app.get("/api/v1/workspace/file")
1250
+ async def read_workspace_file(path: str = Query(...), download: bool = Query(default=False)):
1251
+ """Read one file inside the workspace (or a linked directory) for preview.
1252
+
1253
+ Absolute paths outside the workspace and its linked directories are
1254
+ rejected, as are oversized files; preview clients render the payload
1255
+ according to the returned contentType.
1256
+ """
1257
+ import mimetypes
1258
+ from urllib.parse import quote
1259
+
1260
+ raw = Path(path).expanduser()
1261
+ candidate = raw if raw.is_absolute() else studio.workspace.root / raw
1262
+ try:
1263
+ resolved = candidate.resolve()
1264
+ except OSError as error:
1265
+ raise StudioError("WORKSPACE_FILE_INVALID", "文件路径无效", status_code=422) from error
1266
+
1267
+ allowed_roots = [studio.workspace.root.resolve()]
1268
+ for item in LinkedDirectoryPolicy(studio.workspace.root).list():
1269
+ linked = Path(item.path).expanduser().resolve()
1270
+ if linked.is_dir():
1271
+ allowed_roots.append(linked)
1272
+ if not any(
1273
+ resolved == root or resolved.is_relative_to(root) for root in allowed_roots
1274
+ ):
1275
+ raise StudioError(
1276
+ "WORKSPACE_FILE_FORBIDDEN", "路径不在工作区(或链接目录)内", status_code=403
1277
+ )
1278
+ if not resolved.is_file():
1279
+ raise StudioError("WORKSPACE_FILE_NOT_FOUND", "文件不存在", status_code=404)
1280
+ size = resolved.stat().st_size
1281
+ if size > 2 * 1024 * 1024:
1282
+ raise StudioError(
1283
+ "WORKSPACE_FILE_TOO_LARGE", "文件超过 2MB 预览上限", status_code=413
1284
+ )
1285
+ suffix = resolved.suffix.lower()
1286
+ if suffix in {
1287
+ ".png", ".jpg", ".jpeg", ".gif", ".webp", ".svg", ".bmp", ".ico",
1288
+ }:
1289
+ content_type = mimetypes.guess_type(resolved.name)[0] or "application/octet-stream"
1290
+ media_category = "image"
1291
+ payload: bytes | str = resolved.read_bytes()
1292
+ elif suffix in {".md", ".markdown"}:
1293
+ content_type, media_category = "text/markdown", "markdown"
1294
+ payload = _decode_text_file(resolved)
1295
+ else:
1296
+ text_types = {
1297
+ ".txt", ".json", ".csv", ".tsv", ".log", ".py", ".js", ".ts", ".tsx",
1298
+ ".jsx", ".html", ".css", ".yaml", ".yml", ".toml", ".xml", ".sh",
1299
+ ".sql", ".rs", ".go", ".java", ".c", ".cpp", ".h", ".diff", ".ini",
1300
+ }
1301
+ if suffix not in text_types:
1302
+ raise StudioError(
1303
+ "WORKSPACE_FILE_UNSUPPORTED", "该文件类型暂不支持预览", status_code=415
1304
+ )
1305
+ content_type, media_category = "text/plain", "text"
1306
+ payload = _decode_text_file(resolved)
1307
+ response = {
1308
+ "path": str(resolved),
1309
+ "name": resolved.name,
1310
+ "sizeBytes": size,
1311
+ "contentType": content_type,
1312
+ "mediaCategory": media_category,
1313
+ }
1314
+ if download:
1315
+ quoted = quote(resolved.name)
1316
+ return Response(
1317
+ content=payload if isinstance(payload, bytes) else payload.encode("utf-8"),
1318
+ media_type=content_type,
1319
+ headers={"Content-Disposition": f"attachment; filename*=UTF-8''{quoted}"},
1320
+ )
1321
+ if media_category == "image":
1322
+ import base64
1323
+
1324
+ response["dataBase64"] = base64.b64encode(payload).decode("ascii")
1325
+ else:
1326
+ response["content"] = payload
1327
+ return response
1328
+
868
1329
  @app.post("/api/v1/workspaces:open")
869
1330
  async def open_workspace(payload: WorkspaceOpenRequest):
870
- # This endpoint only reconnects to the daemon's already-bound root. Do
871
- # not resolve or otherwise touch a caller-provided filesystem path.
872
- requested = os.path.normcase(os.path.abspath(os.path.expanduser(payload.path)))
873
- bound_root = os.path.normcase(str(studio.workspace.root))
874
- if requested != bound_root:
1331
+ try:
1332
+ # A user-selected directory is itself the workspace. The registry
1333
+ # entry is created as part of opening it; callers do not need to
1334
+ # pre-register paths in a separate settings screen.
1335
+ record = studio.switch(payload.path, create=True)
1336
+ # Start local state immediately, but keep optional DSH/Profile
1337
+ # discovery off the workspace-switch response path.
1338
+ runtime = studio.active
1339
+ await runtime.start(wait_for_dsh=False)
1340
+ schedule_runtime_warmup = getattr(app.state, "runtime_warmups", None)
1341
+ if schedule_runtime_warmup is not None:
1342
+ schedule_runtime_warmup(runtime)
1343
+ except FileNotFoundError as error:
1344
+ raise StudioError("WORKSPACE_NOT_FOUND", "工作区目录不存在", status_code=404) from error
1345
+ response = {
1346
+ "name": record.name,
1347
+ "path": record.path,
1348
+ "workspaceId": record.workspace_id,
1349
+ }
1350
+ # Preserve the legacy reconnect response shape for the daemon root;
1351
+ # switched workspaces include the stable identity for scoped clients.
1352
+ if os.path.normcase(record.path) == os.path.normcase(str(initial_service.workspace.root)):
1353
+ response.pop("workspaceId", None)
1354
+ return response
1355
+
1356
+ @app.post("/api/v1/workspaces:choose")
1357
+ async def choose_workspace_directory():
1358
+ """Open the host folder chooser for a local Web Studio session."""
1359
+ if sys.platform != "darwin":
1360
+ raise StudioError(
1361
+ "WORKSPACE_PICKER_UNAVAILABLE",
1362
+ "当前系统没有可用的原生目录选择器,请输入目录路径",
1363
+ status_code=501,
1364
+ )
1365
+ script = (
1366
+ 'tell application "System Events" to set selectedFolder to choose folder '
1367
+ 'with prompt "选择 AgentKit Studio 工作区"\n'
1368
+ 'POSIX path of selectedFolder'
1369
+ )
1370
+ result = await asyncio.to_thread(
1371
+ subprocess.run,
1372
+ ["osascript", "-e", script],
1373
+ capture_output=True,
1374
+ text=True,
1375
+ check=False,
1376
+ )
1377
+ if result.returncode != 0:
1378
+ return {"path": None, "cancelled": True}
1379
+ path = result.stdout.strip()
1380
+ return {"path": path or None, "cancelled": not bool(path)}
1381
+
1382
+ @app.get("/api/v1/workspaces")
1383
+ async def list_workspaces():
1384
+ active_path = os.path.normcase(str(studio.workspace.root))
1385
+ items = []
1386
+ for record in WorkspaceRegistry().list():
1387
+ item = record.__dict__.copy()
1388
+ item["active"] = os.path.normcase(record.path) == active_path
1389
+ items.append(item)
1390
+ return {"items": items}
1391
+
1392
+ @app.post("/api/v1/workspaces/{workspace_id}/linked-directories")
1393
+ async def add_linked_directory(workspace_id: str, payload: LinkedDirectoryRequest):
1394
+ record = next(
1395
+ (r for r in WorkspaceRegistry().list() if r.workspace_id == workspace_id), None
1396
+ )
1397
+ if record is None or os.path.normcase(record.path) != os.path.normcase(
1398
+ str(studio.workspace.root)
1399
+ ):
875
1400
  raise StudioError(
876
- "WORKSPACE_PATH_FORBIDDEN",
877
- "当前 Daemon 不允许切换到启动 root 之外的工作区",
878
- status_code=403,
1401
+ "WORKSPACE_NOT_FOUND", "工作区不存在或不属于当前 Studio", status_code=404
1402
+ )
1403
+ item = LinkedDirectoryPolicy(studio.workspace.root).set(
1404
+ payload.path, payload.mode, payload.label
1405
+ )
1406
+ return item.__dict__
1407
+
1408
+ @app.get("/api/v1/workspaces/{workspace_id}/linked-directories")
1409
+ async def list_linked_directories(workspace_id: str):
1410
+ record = next(
1411
+ (r for r in WorkspaceRegistry().list() if r.workspace_id == workspace_id), None
1412
+ )
1413
+ if record is None or os.path.normcase(record.path) != os.path.normcase(
1414
+ str(studio.workspace.root)
1415
+ ):
1416
+ raise StudioError(
1417
+ "WORKSPACE_NOT_FOUND", "工作区不存在或不属于当前 Studio", status_code=404
879
1418
  )
880
1419
  return {
881
- "name": studio.workspace.root.name,
882
- "path": str(studio.workspace.root),
1420
+ "items": [item.__dict__ for item in LinkedDirectoryPolicy(studio.workspace.root).list()]
883
1421
  }
884
1422
 
1423
+ @app.delete("/api/v1/workspaces/{workspace_id}/linked-directories")
1424
+ async def remove_linked_directory(workspace_id: str, path: str):
1425
+ record = next(
1426
+ (r for r in WorkspaceRegistry().list() if r.workspace_id == workspace_id), None
1427
+ )
1428
+ if record is None or os.path.normcase(record.path) != os.path.normcase(
1429
+ str(studio.workspace.root)
1430
+ ):
1431
+ raise StudioError(
1432
+ "WORKSPACE_NOT_FOUND", "工作区不存在或不属于当前 Studio", status_code=404
1433
+ )
1434
+ return {"removed": LinkedDirectoryPolicy(studio.workspace.root).remove(path)}
1435
+
1436
+ @app.get("/api/v1/workspaces/{workspace_id}/linked-directories/authorize")
1437
+ async def authorize_linked_directory(
1438
+ workspace_id: str,
1439
+ path: str,
1440
+ operation: Literal["read", "write"] = "read",
1441
+ ):
1442
+ record = next(
1443
+ (r for r in WorkspaceRegistry().list() if r.workspace_id == workspace_id), None
1444
+ )
1445
+ if record is None or os.path.normcase(record.path) != os.path.normcase(
1446
+ str(studio.workspace.root)
1447
+ ):
1448
+ raise StudioError(
1449
+ "WORKSPACE_NOT_FOUND", "工作区不存在或不属于当前 Studio", status_code=404
1450
+ )
1451
+ allowed = LinkedDirectoryPolicy(studio.workspace.root).authorize(path, operation)
1452
+ return {"path": path, "operation": operation, "allowed": allowed}
1453
+
885
1454
  @app.get("/api/v1/codex/manifest")
886
1455
  async def get_codex_manifest():
887
1456
  return studio.codex_manifest_state()
@@ -970,6 +1539,7 @@ def create_studio_app(
970
1539
  unrelated Build.
971
1540
  """
972
1541
 
1542
+ await runtime_model_catalog()
973
1543
  build = await studio.ensure_current_build(agent_id)
974
1544
  return {
975
1545
  "buildId": build.id,
@@ -1205,9 +1775,53 @@ def create_studio_app(
1205
1775
  async def get_agent(agent_id: str):
1206
1776
  return studio.agent_detail(agent_id)
1207
1777
 
1778
+ @app.post("/api/v1/agents/{agent_id}/runtime:prewarm", status_code=202)
1779
+ async def prewarm_agent_runtime(agent_id: str, payload: dict[str, Any]):
1780
+ """后台预热当前 Build 的 Provider 激活(MCP/图编译),不执行任何 turn。
1781
+
1782
+ 首 token 延迟的主因是首轮 open_activation 时的 MCP spawn/health/list
1783
+ (实测 ~12s)。前端在进入会话时 fire-and-forget 调用本端点,把这段
1784
+ 开销移到用户输入之前;失败静默(首轮照旧现场预热)。
1785
+ """
1786
+ session_id = str(payload.get("sessionId") or "")
1787
+ if not session_id:
1788
+ raise StudioError(
1789
+ "SESSION_ID_REQUIRED", "缺少 sessionId", status_code=422
1790
+ )
1791
+ try:
1792
+ build = await studio.ensure_current_build(agent_id)
1793
+ except StudioError as exc:
1794
+ raise StudioError(
1795
+ "BUILD_NOT_FOUND", f"Agent 尚无可运行的 Build:{exc.message}",
1796
+ status_code=404,
1797
+ ) from exc
1798
+
1799
+ async def warm() -> None:
1800
+ try:
1801
+ spec = studio.resolve_run_spec(build.id)
1802
+ runtime = studio.run_service.plugin_runtime
1803
+ if runtime is None:
1804
+ return
1805
+ kernel_adapter = getattr(runtime, "kernel_adapter", None)
1806
+ if kernel_adapter is None:
1807
+ return
1808
+ # 打开并保留激活(以 session 为 key);下一轮 RunAgent 直接复用,
1809
+ # 不再现场 spawn/health/list MCP。
1810
+ await kernel_adapter(spec, session_id=session_id)
1811
+ except Exception as error:
1812
+ logger.info("runtime prewarm for %s skipped: %s", agent_id, error)
1813
+
1814
+ task = asyncio.create_task(warm())
1815
+ app.state.runtime_prewarms = getattr(app.state, "runtime_prewarms", set())
1816
+ app.state.runtime_prewarms.add(task)
1817
+ task.add_done_callback(app.state.runtime_prewarms.discard)
1818
+ return {"status": "prewarming", "buildId": build.id}
1819
+
1208
1820
  @app.get("/api/v1/agents/{agent_id}/models")
1209
1821
  async def get_agent_models(agent_id: str):
1210
- return shared_web.list_models(shared_web.resolve_agent_id(agent_id))
1822
+ model_agent_id = shared_web.resolve_agent_id(agent_id)
1823
+ await runtime_model_catalog()
1824
+ return shared_web.list_models(model_agent_id)
1211
1825
 
1212
1826
  @app.put("/api/v1/agents/{agent_id}")
1213
1827
  async def update_agent(
@@ -1308,6 +1922,7 @@ def create_studio_app(
1308
1922
  payload: BuildRequest,
1309
1923
  idempotency_key: str | None = Header(default=None, alias="Idempotency-Key"),
1310
1924
  ):
1925
+ await runtime_model_catalog()
1311
1926
  return studio.submit_studio_build(
1312
1927
  agent_id,
1313
1928
  revision=payload.revision,
@@ -1360,19 +1975,34 @@ def create_studio_app(
1360
1975
 
1361
1976
  @app.get("/api/v1/runs/{run_id}")
1362
1977
  async def get_run(run_id: str):
1363
- return studio.event_store.get(run_id)
1978
+ runtime = studio.runtime_for_run(run_id)
1979
+ if runtime is None:
1980
+ raise StudioError("RUN_NOT_FOUND", "运行不存在", status_code=404)
1981
+ return runtime.event_store.get(run_id)
1364
1982
 
1365
1983
  @app.post("/api/v1/runs/{run_id}:cancel", status_code=202)
1366
1984
  async def cancel_run(run_id: str):
1367
- return await studio.run_service.cancel_run(run_id)
1985
+ runtime = studio.runtime_for_run(run_id)
1986
+ if runtime is None:
1987
+ raise StudioError("RUN_NOT_FOUND", "运行不存在", status_code=404)
1988
+ runtime._require_direct_run(run_id)
1989
+ return await runtime.run_service.cancel_run(run_id)
1368
1990
 
1369
1991
  @app.post("/api/v1/runs/{run_id}:pause", status_code=202)
1370
1992
  async def pause_run(run_id: str):
1371
- return await studio.run_service.pause_run(run_id)
1993
+ runtime = studio.runtime_for_run(run_id)
1994
+ if runtime is None:
1995
+ raise StudioError("RUN_NOT_FOUND", "运行不存在", status_code=404)
1996
+ runtime._require_direct_run(run_id)
1997
+ return await runtime.run_service.pause_run(run_id)
1372
1998
 
1373
1999
  @app.post("/api/v1/runs/{run_id}:resume", status_code=202)
1374
2000
  async def resume_run(run_id: str):
1375
- return await studio.run_service.resume_run(run_id)
2001
+ runtime = studio.runtime_for_run(run_id)
2002
+ if runtime is None:
2003
+ raise StudioError("RUN_NOT_FOUND", "运行不存在", status_code=404)
2004
+ runtime._require_direct_run(run_id)
2005
+ return await runtime.run_service.resume_run(run_id)
1376
2006
 
1377
2007
  @app.post("/api/v1/runs/{run_id}/interactions/{interaction_id}:submit")
1378
2008
  async def submit_run_interaction(
@@ -1380,7 +2010,10 @@ def create_studio_app(
1380
2010
  interaction_id: str,
1381
2011
  payload: InteractionSubmitRequest,
1382
2012
  ):
1383
- return await studio.run_service.submit_interaction(
2013
+ runtime = studio.runtime_for_run(run_id)
2014
+ if runtime is None:
2015
+ raise StudioError("RUN_NOT_FOUND", "运行不存在", status_code=404)
2016
+ return await runtime.run_service.submit_interaction(
1384
2017
  run_id,
1385
2018
  interaction_id,
1386
2019
  name=payload.name,
@@ -1392,7 +2025,10 @@ def create_studio_app(
1392
2025
  @app.get("/api/v1/runs/{run_id}/context")
1393
2026
  async def get_run_context(run_id: str):
1394
2027
  """Runtime Context Evidence:planned/projected/actual + 精度 + ownership。"""
1395
- record = studio.event_store.get(run_id)
2028
+ runtime = studio.runtime_for_run(run_id)
2029
+ if runtime is None:
2030
+ raise StudioError("RUN_NOT_FOUND", "运行不存在", status_code=404)
2031
+ record = runtime.event_store.get(run_id)
1396
2032
  plan = record.context_plan or {}
1397
2033
  evidence = record.prompt_evidence or {}
1398
2034
  return {
@@ -1446,7 +2082,10 @@ def create_studio_app(
1446
2082
 
1447
2083
  @app.delete("/api/v1/sessions/{session_id}", status_code=204)
1448
2084
  async def delete_studio_session(session_id: str):
1449
- await studio.delete_session(session_id)
2085
+ runtime = await studio.runtime_for_session(session_id)
2086
+ if runtime is None:
2087
+ raise StudioError("SESSION_NOT_FOUND", "会话不存在", status_code=404)
2088
+ await runtime.delete_session(session_id)
1450
2089
  return Response(status_code=204)
1451
2090
 
1452
2091
  @app.get("/api/v1/sessions/{session_id}/events")
@@ -1456,7 +2095,10 @@ def create_studio_app(
1456
2095
  invocation_id: str | None = Query(default=None, alias="invocationId"),
1457
2096
  limit: int = Query(default=100, ge=1, le=500),
1458
2097
  ):
1459
- return await studio.trajectory_page(
2098
+ runtime = await studio.runtime_for_session(session_id)
2099
+ if runtime is None:
2100
+ raise StudioError("SESSION_NOT_FOUND", "会话不存在", status_code=404)
2101
+ return await runtime.trajectory_page(
1460
2102
  session_id,
1461
2103
  before_seq_id=before_seq_id,
1462
2104
  invocation_id=invocation_id,
@@ -1470,10 +2112,13 @@ def create_studio_app(
1470
2112
  after_seq_id: int = Query(default=0, ge=0, alias="afterSeqId"),
1471
2113
  invocation_id: str | None = Query(default=None, alias="invocationId"),
1472
2114
  ):
1473
- await studio._require_runtime_session(session_id)
2115
+ runtime = await studio.runtime_for_session(session_id)
2116
+ if runtime is None:
2117
+ raise StudioError("SESSION_NOT_FOUND", "会话不存在", status_code=404)
2118
+ await runtime._require_runtime_session(session_id)
1474
2119
  last = request.headers.get("Last-Event-ID")
1475
2120
  cursor = int(last) if last and last.isdigit() else after_seq_id
1476
- stream = studio.stream_trajectory(
2121
+ stream = runtime.stream_trajectory(
1477
2122
  session_id,
1478
2123
  cursor,
1479
2124
  invocation_id=invocation_id,
@@ -1544,6 +2189,44 @@ def create_studio_app(
1544
2189
  async def list_runs(session_id: str | None = Query(default=None, alias="sessionId")):
1545
2190
  return {"items": studio.event_store.list_runs(session_id=session_id)}
1546
2191
 
2192
+ @app.get("/api/v1/workspaces/runs")
2193
+ async def list_workspace_runs():
2194
+ """后台入口:聚合所有已打开 workspace 的运行,并保留归属。"""
2195
+ return {"items": studio.all_runs()}
2196
+
2197
+ @app.get("/api/v1/workspaces/operations")
2198
+ async def list_workspace_operations():
2199
+ return {"items": studio.all_operations()}
2200
+
2201
+ @app.get("/api/v1/runs/{run_id}/documents/metadata")
2202
+ def run_document_metadata(run_id: str, path: str, request: Request):
2203
+ return document_metadata(studio, run_id, path, local=is_local_desktop_request(request))
2204
+
2205
+ @app.post("/api/v1/runs/{run_id}/documents/actions")
2206
+ def run_document_action(run_id: str, payload: NativeDocumentActionRequest, request: Request):
2207
+ return perform_document_action(
2208
+ studio, run_id, payload, local=is_local_desktop_request(request)
2209
+ )
2210
+
2211
+ @app.get("/api/v1/runs/{run_id}/documents/content")
2212
+ def run_document(run_id: str, path: str, preview: bool = False):
2213
+ file_path, content = read_document(studio, run_id, path)
2214
+ if preview:
2215
+ return JSONResponse(
2216
+ {"name": file_path.name, "content": content},
2217
+ headers={"Cache-Control": "no-store"},
2218
+ )
2219
+ return Response(
2220
+ content,
2221
+ media_type="text/markdown",
2222
+ headers={
2223
+ "Content-Disposition": "attachment; filename*=UTF-8''"
2224
+ + quote(file_path.name),
2225
+ "X-Content-Type-Options": "nosniff",
2226
+ "Cache-Control": "no-store",
2227
+ },
2228
+ )
2229
+
1547
2230
  @app.get("/api/v1/runs/{run_id}/events")
1548
2231
  async def run_events(
1549
2232
  run_id: str,
@@ -1552,7 +2235,10 @@ def create_studio_app(
1552
2235
  ):
1553
2236
  last = request.headers.get("Last-Event-ID")
1554
2237
  cursor = int(last) if last and last.isdigit() else after
1555
- events = await studio.run_service.events(run_id, after=cursor)
2238
+ runtime = studio.runtime_for_run(run_id)
2239
+ if runtime is None:
2240
+ raise StudioError("RUN_NOT_FOUND", "运行不存在", status_code=404)
2241
+ events = await runtime.run_service.events(run_id, after=cursor)
1556
2242
  return _sse(events)
1557
2243
 
1558
2244
  @app.get("/api/v1/traces/overview")
@@ -1567,6 +2253,10 @@ def create_studio_app(
1567
2253
  status=status,
1568
2254
  )
1569
2255
 
2256
+ @app.get("/api/v1/workspaces/traces")
2257
+ async def list_workspace_traces():
2258
+ return {"items": studio.all_traces()}
2259
+
1570
2260
  @app.get("/api/v1/traces")
1571
2261
  async def list_traces(
1572
2262
  agent_id: str | None = Query(default=None, alias="agentId"),
@@ -1668,7 +2358,7 @@ def create_studio_app(
1668
2358
  async def list_deployments():
1669
2359
  """Read local deployment receipts without implicit cloud refreshes."""
1670
2360
 
1671
- return {"items": studio.cloud.list()}
2361
+ return {"items": studio.cloud.list(), "currentIdentity": studio.cloud.cached_identity()}
1672
2362
 
1673
2363
  @app.get("/api/v1/cloud-agents")
1674
2364
  async def list_account_cloud_agents(
@@ -2010,11 +2700,17 @@ def create_studio_app(
2010
2700
 
2011
2701
  @app.get("/api/v1/operations/{operation_id}")
2012
2702
  async def get_operation(operation_id: str):
2013
- return studio.operations.get(operation_id)
2703
+ runtime = studio.runtime_for_operation(operation_id)
2704
+ if runtime is None:
2705
+ raise StudioError("OPERATION_NOT_FOUND", "操作不存在", status_code=404)
2706
+ return runtime.operations.get(operation_id)
2014
2707
 
2015
2708
  @app.post("/api/v1/operations/{operation_id}:cancel")
2016
2709
  async def cancel_operation(operation_id: str):
2017
- return studio.operations.cancel(operation_id)
2710
+ runtime = studio.runtime_for_operation(operation_id)
2711
+ if runtime is None:
2712
+ raise StudioError("OPERATION_NOT_FOUND", "操作不存在", status_code=404)
2713
+ return runtime.operations.cancel(operation_id)
2018
2714
 
2019
2715
  @app.get("/api/v1/operations/{operation_id}/events")
2020
2716
  async def operation_events(
@@ -2024,7 +2720,10 @@ def create_studio_app(
2024
2720
  ):
2025
2721
  last = request.headers.get("Last-Event-ID")
2026
2722
  cursor = int(last) if last and last.isdigit() else after
2027
- events = studio.operations.events(operation_id, after=cursor)
2723
+ runtime = studio.runtime_for_operation(operation_id)
2724
+ if runtime is None:
2725
+ raise StudioError("OPERATION_NOT_FOUND", "操作不存在", status_code=404)
2726
+ events = runtime.operations.events(operation_id, after=cursor)
2028
2727
  if "application/json" in request.headers.get("Accept", ""):
2029
2728
  return {"items": events}
2030
2729
  return _sse(events)
@@ -2035,6 +2734,24 @@ def create_studio_app(
2035
2734
  runtime_model_catalog=runtime_model_catalog,
2036
2735
  )
2037
2736
  register_memory_routes(app, studio)
2737
+ register_resource_connection_routes(app, studio)
2038
2738
  register_plugin_routes(app, studio)
2739
+ async def workspace_plugin_api(scope, receive, send):
2740
+ # The plugin registry belongs to the active workspace. Mounting the
2741
+ # initial service registry directly would keep lifecycle/routes pinned
2742
+ # to the first folder after a workspace switch.
2743
+ await studio.active.workspace_plugins.api(scope, receive, send)
2744
+
2745
+ app.mount("/api/v1", workspace_plugin_api, name="workspace-plugin-api")
2746
+
2747
+ from ksadk.studio.dsh_application import register_dsh_application
2748
+
2749
+ register_dsh_application(
2750
+ app,
2751
+ studio,
2752
+ session_secret=session_secret,
2753
+ security_enabled=security_enabled,
2754
+ fallback_url="/studio-shell/",
2755
+ )
2039
2756
 
2040
2757
  return app