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
@@ -13,6 +13,7 @@ import json
13
13
  import os
14
14
  import re
15
15
  import shutil
16
+ import signal
16
17
  import stat
17
18
  import subprocess
18
19
  import tempfile
@@ -28,8 +29,16 @@ try:
28
29
  except ImportError: # pragma: no cover - DSH production hosts are Unix
29
30
  fcntl = None # type: ignore[assignment]
30
31
 
32
+ import yaml
31
33
  from pydantic import BaseModel, ConfigDict
32
34
 
35
+ from ksadk.plugins.dsh_installation_digest import (
36
+ DshInstallationDigestError,
37
+ installation_digest,
38
+ )
39
+
40
+ _COMMAND_TIMEOUT_SECONDS = 120
41
+
33
42
 
34
43
  def _to_camel(value: str) -> str:
35
44
  head, *tail = value.split("_")
@@ -52,18 +61,6 @@ class DshBridgeHost(_DshModel):
52
61
  available: Literal[True] = True
53
62
 
54
63
 
55
- class DshClientBundle(_DshModel):
56
- """One validated browser half from an installed DSH package."""
57
-
58
- platform: Literal["web"] = "web"
59
- digest: str
60
- content_bytes: int
61
- external: tuple[str, ...] = ()
62
- inject: tuple[str, ...] = ()
63
- compatible: bool
64
- incompatibility_reason: str = ""
65
-
66
-
67
64
  class DshPluginInventory(_DshModel):
68
65
  ecosystem: Literal["dsh"] = "dsh"
69
66
  integration_mode: Literal["bridged"] = "bridged"
@@ -75,10 +72,11 @@ class DshPluginInventory(_DshModel):
75
72
  requested_spec: str
76
73
  source_digest: str | None = None
77
74
  source_kind: Literal["directory", "tgz"] | None = None
75
+ client_extension: bool = False
76
+ settings_integration: bool = False
78
77
  installed: Literal[True] = True
79
78
  enabled: bool
80
79
  permissions_declared: Literal[False] = False
81
- client_bundle: DshClientBundle | None = None
82
80
  risk_disclosures: tuple[str, ...] = (
83
81
  "DSH packages and install scripts run with the native host user privileges.",
84
82
  "DSH bundle manifests do not declare a complete runtime permission set.",
@@ -93,6 +91,19 @@ class DshProfileProjection(_DshModel):
93
91
  host_version: str
94
92
 
95
93
 
94
+ class DshProfileBuildSnapshot(_DshModel):
95
+ """Credential-free identity of the actual local profile execution inputs."""
96
+
97
+ projection: DshProfileProjection
98
+ dependency_lock_digest: str
99
+ installation_digest: str
100
+
101
+ @property
102
+ def digest(self) -> str:
103
+ payload = json.dumps(self.model_dump(mode="json"), sort_keys=True, separators=(",", ":"))
104
+ return "sha256:" + hashlib.sha256(payload.encode()).hexdigest()
105
+
106
+
96
107
  class DshBridgeError(RuntimeError):
97
108
  """Base failure for one bounded DSH profile operation."""
98
109
 
@@ -113,6 +124,10 @@ class DshPluginMutationError(DshBridgeError):
113
124
  pass
114
125
 
115
126
 
127
+ class DshProfileRecoveryError(DshPluginMutationError):
128
+ """The owner must keep run admission suspended until profile recovery."""
129
+
130
+
116
131
  class _CommandResult(_DshModel):
117
132
  stdout: str = ""
118
133
  stderr: str = ""
@@ -122,13 +137,16 @@ CommandRunner = Callable[[Sequence[str], Path, Mapping[str, str]], _CommandResul
122
137
 
123
138
  _PROFILE_NAME = re.compile(r"^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$")
124
139
  _PACKAGE_NAME = re.compile(r"^(?:@[A-Za-z0-9._-]+/)?[A-Za-z0-9._-]+$")
140
+ _PINNED_REGISTRY_SOURCE = re.compile(
141
+ r"^(?:@[A-Za-z0-9._-]+/)?[A-Za-z0-9._-]+@"
142
+ r"\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$"
143
+ )
125
144
  _HOST_VERSION = re.compile(r"\b(\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?)\b")
126
145
  _STATE_FILE = ".ksadk-dsh-plugins.json"
127
146
  _IMMUTABLE_SOURCE_DIR = "immutable-plugin-sources"
128
147
  _SNAPSHOT_FILES = ("package.json", "pnpm-lock.yaml", "pnpm-workspace.yaml", _STATE_FILE)
148
+ _WEB_PROFILE_BUILTINS = ("@deepseek-ai/dsh-base", "@deepseek-ai/dsh-web-app")
129
149
  _MAX_JSON_BYTES = 2 * 1024 * 1024
130
- _MAX_CLIENT_BUNDLE_BYTES = 8 * 1024 * 1024
131
- _STUDIO_CLIENT_EXTERNALS = frozenset({"react"})
132
150
  _PROFILE_LOCK_DIR = "profile-locks"
133
151
  _DSH_SUBPROCESS_ENV_KEYS = (
134
152
  "PATH",
@@ -165,6 +183,26 @@ _DSH_SUBPROCESS_ENV_KEYS = (
165
183
  )
166
184
 
167
185
 
186
+ def validate_dsh_registry_source(source: str) -> str:
187
+ """Return one exact, credential-free npm registry package coordinate."""
188
+
189
+ value = source.strip()
190
+ if not _PINNED_REGISTRY_SOURCE.fullmatch(value):
191
+ raise ValueError(
192
+ "DSH registry source must be <package>@<exact-semver>; "
193
+ "Git URLs, tags, and ranges are not allowed"
194
+ )
195
+ return value
196
+
197
+
198
+ def validate_dsh_registry_request(source: str) -> str:
199
+ """Accept a package name for latest resolution, or an exact coordinate."""
200
+ value = source.strip()
201
+ if len(value) <= 214 and not value.startswith("-") and _PACKAGE_NAME.fullmatch(value):
202
+ return value
203
+ return validate_dsh_registry_source(value)
204
+
205
+
168
206
  def dsh_subprocess_environment(*, dsh_home: Path | None = None) -> dict[str, str]:
169
207
  """Build the explicit environment inherited by DSH and pnpm children.
170
208
 
@@ -205,6 +243,12 @@ class _PreparedSource:
205
243
  artifact: str
206
244
 
207
245
 
246
+ @dataclass(frozen=True)
247
+ class _ProfileSnapshot:
248
+ files: Mapping[str, bytes | None]
249
+ node_modules_existed: bool
250
+
251
+
208
252
  class DshProfilePluginBridge:
209
253
  """Manage DSH bundles in one isolated Profile with rollback and preflight."""
210
254
 
@@ -212,7 +256,7 @@ class DshProfilePluginBridge:
212
256
  self,
213
257
  *,
214
258
  dsh_home: Path,
215
- profile: str = "ksadk",
259
+ profile: str = "web",
216
260
  dsh_command: Sequence[str] | None = None,
217
261
  command_runner: CommandRunner | None = None,
218
262
  cwd: Path | None = None,
@@ -276,6 +320,14 @@ class DshProfilePluginBridge:
276
320
  package = self._read_package(name)
277
321
  if package is None or self._bundle_patch(package) is None:
278
322
  continue
323
+ dsh = package.get("dsh")
324
+ client = dsh.get("client") if isinstance(dsh, dict) else None
325
+ inject = client.get("inject") if isinstance(client, dict) else None
326
+ client_inject = (
327
+ tuple(item for item in inject if isinstance(item, str))
328
+ if isinstance(inject, list)
329
+ else ()
330
+ )
279
331
  receipt = state["sources"].get(name)
280
332
  items.append(
281
333
  DshPluginInventory(
@@ -289,8 +341,12 @@ class DshProfilePluginBridge:
289
341
  requested_spec=requested_spec,
290
342
  source_digest=receipt["digest"] if receipt is not None else None,
291
343
  source_kind=receipt["kind"] if receipt is not None else None,
344
+ client_extension=isinstance(client, dict),
345
+ settings_integration=any(
346
+ "client-ui-settings" in item or "client-ui-slots" in item
347
+ for item in client_inject
348
+ ),
292
349
  enabled=name in active and name not in set(state["disabled"]),
293
- client_bundle=self._client_bundle_metadata(name, package),
294
350
  )
295
351
  )
296
352
  return tuple(sorted(items, key=lambda item: (item.display_name.casefold(), item.name)))
@@ -314,6 +370,7 @@ class DshProfilePluginBridge:
314
370
  "explicit approval is required"
315
371
  )
316
372
  self._validate_source(source)
373
+ source = self._resolve_install_source(source.strip())
317
374
  with self._profile_transaction(exclusive=True):
318
375
  self._require_package_mutation_rollback(new_profile_allowed=True)
319
376
  snapshot = self._snapshot()
@@ -492,36 +549,141 @@ class DshProfilePluginBridge:
492
549
  host_version=self.host.version,
493
550
  )
494
551
 
495
- def read_client_bundle(self, name: str, *, expected_digest: str) -> bytes:
496
- """Read one immutable browser artifact after revalidating Profile inventory."""
552
+ def migrate_to_isolated_layout(
553
+ self,
554
+ *,
555
+ accept_host_permissions: bool = False,
556
+ recover_external_dependency_links: bool = False,
557
+ ) -> None:
558
+ """Migrate a stopped profile; the caller owns Core/admission suspension.
497
559
 
560
+ Keep an independent byte copy for rollback: a frozen reinstall of the
561
+ old layout can itself hit the pinned pnpm shutdown defect. Studio-owned
562
+ profiles may also repair an isolated tree polluted by a legacy Core
563
+ module-fallback cache.
564
+ """
565
+ if not accept_host_permissions:
566
+ raise DshPluginApprovalRequired("Profile migration may run package install scripts")
567
+ with self._profile_transaction(exclusive=True):
568
+ self._require_package_mutation_rollback(new_profile_allowed=False)
569
+ manifest = self._read_manifest()
570
+ self._verify_source_receipts(manifest, self._read_state(manifest))
571
+ settings_path = self._profile_root / "pnpm-workspace.yaml"
572
+ settings = yaml.safe_load(settings_path.read_text(encoding="utf-8"))
573
+ if not isinstance(settings, dict):
574
+ raise DshPluginMutationError("Invalid package-manager settings")
575
+ node_linker = settings.get("nodeLinker")
576
+ if node_linker not in {"hoisted", "isolated"}:
577
+ raise DshPluginMutationError(
578
+ "Only hoisted or isolated profiles can use this migration"
579
+ )
580
+ modules = self._profile_root / "node_modules"
581
+ if modules.is_symlink() or not modules.is_dir():
582
+ raise DshPluginMutationError("Profile installation is not a real directory")
583
+ # A normal migration only accepts a closed tree. Studio may repair
584
+ # its own legacy hoisted Profile when pnpm linked a dependency to
585
+ # an ambient store: move that tree atomically instead of reading or
586
+ # following the external target, then reinstall from the frozen lock.
587
+ moved_unsafe_tree = False
588
+ try:
589
+ original_digest = installation_digest(modules)
590
+ except DshInstallationDigestError as error:
591
+ if (
592
+ not recover_external_dependency_links
593
+ or str(error) != "DSH dependency link escapes the installed tree"
594
+ ):
595
+ raise
596
+ original_digest = None
597
+ moved_unsafe_tree = True
598
+ if node_linker == "isolated" and not moved_unsafe_tree:
599
+ self._preflight()
600
+ return
601
+ fallback = self._profile_root / ".dsh-module-fallback"
602
+ if fallback.exists() and (fallback.is_symlink() or not fallback.is_dir()):
603
+ raise DshPluginMutationError("DSH module fallback is not a real directory")
604
+ snapshot = self._snapshot()
605
+ root = tempfile.mkdtemp(prefix=".layout-backup-", dir=self._profile_root)
606
+ preserve_backup = False
607
+ try:
608
+ backup = Path(root) / "node_modules"
609
+ fallback_backup = Path(root) / "module-fallback"
610
+ if moved_unsafe_tree:
611
+ os.replace(modules, backup)
612
+ else:
613
+ shutil.copytree(modules, backup, symlinks=True)
614
+ if installation_digest(backup) != original_digest:
615
+ raise DshPluginMutationError("Profile installation backup did not verify")
616
+ fallback_moved = fallback.is_dir()
617
+ if fallback_moved:
618
+ os.replace(fallback, fallback_backup)
619
+ original_files = Path(root) / "profile-files"
620
+ original_files.mkdir(mode=0o700)
621
+ for name, content in snapshot.files.items():
622
+ if content is not None:
623
+ original_path = original_files / name
624
+ original_path.write_bytes(content)
625
+ original_path.chmod(0o600)
626
+ try:
627
+ if not moved_unsafe_tree:
628
+ shutil.rmtree(modules)
629
+ settings["nodeLinker"] = "isolated"
630
+ settings_path.write_text(yaml.safe_dump(settings), encoding="utf-8")
631
+ settings_path.chmod(0o600)
632
+ self._plugin_command("install", "--frozen-lockfile")
633
+ current_lock = (self._profile_root / "pnpm-lock.yaml").read_bytes()
634
+ if current_lock != snapshot.files["pnpm-lock.yaml"]:
635
+ raise DshPluginMutationError("Layout migration changed the dependency lock")
636
+ # Native reconcile can re-enable previously disabled bundles.
637
+ for name in ("package.json", _STATE_FILE):
638
+ content = snapshot.files[name]
639
+ path = self._profile_root / name
640
+ if content is None:
641
+ path.unlink(missing_ok=True)
642
+ else:
643
+ path.write_bytes(content)
644
+ path.chmod(0o600)
645
+ self._verify_source_receipts(manifest, self._read_state(manifest))
646
+ self._preflight()
647
+ except BaseException as error:
648
+ try:
649
+ if modules.is_symlink():
650
+ modules.unlink()
651
+ elif modules.exists():
652
+ shutil.rmtree(modules)
653
+ os.replace(backup, modules)
654
+ if fallback.is_symlink():
655
+ fallback.unlink()
656
+ elif fallback.exists():
657
+ shutil.rmtree(fallback)
658
+ if fallback_moved:
659
+ os.replace(fallback_backup, fallback)
660
+ self._rollback(snapshot, error, reinstall=False)
661
+ except BaseException as recovery_error:
662
+ preserve_backup = True
663
+ raise DshProfileRecoveryError(
664
+ "Profile layout recovery failed; retain the layout backup for recovery"
665
+ ) from recovery_error
666
+ raise
667
+ finally:
668
+ if not preserve_backup:
669
+ shutil.rmtree(root)
670
+
671
+ def snapshot_for_build(self) -> DshProfileBuildSnapshot:
672
+ """Capture exact local inputs under the same profile mutation lock."""
498
673
  with self._profile_transaction(exclusive=False):
499
- return self._read_client_bundle_locked(name, expected_digest=expected_digest)
500
-
501
- def _read_client_bundle_locked(self, name: str, *, expected_digest: str) -> bytes:
502
- item = self.get_plugin(name)
503
- if not item.enabled:
504
- raise DshPluginNotFoundError(f"DSH plugin {name!r} is disabled")
505
- client = item.client_bundle
506
- if client is None or not client.compatible:
507
- raise DshPluginNotFoundError(
508
- f"DSH plugin {name!r} has no Studio-compatible client bundle"
674
+ projection = self.project_profile()
675
+ lock = self._profile_root / "pnpm-lock.yaml"
676
+ if lock.is_symlink() or not lock.is_file() or lock.stat().st_size > 16 * 1024 * 1024:
677
+ raise DshPluginMutationError("DSH Build requires a bounded dependency lockfile")
678
+ return DshProfileBuildSnapshot(
679
+ projection=projection,
680
+ dependency_lock_digest="sha256:" + hashlib.sha256(lock.read_bytes()).hexdigest(),
681
+ installation_digest=installation_digest(self._profile_root / "node_modules"),
509
682
  )
510
- if client.digest != expected_digest:
511
- raise DshPluginMutationError("DSH client bundle digest fence does not match")
512
- package = self._read_package(name)
513
- assert package is not None
514
- path = self._client_bundle_path(name, package)
515
- if path is None:
516
- raise DshPluginNotFoundError(f"DSH plugin {name!r} client bundle is unavailable")
517
- try:
518
- content = path.read_bytes()
519
- except OSError as error:
520
- raise DshPluginMutationError("DSH client bundle became unreadable") from error
521
- actual_digest = f"sha256:{hashlib.sha256(content).hexdigest()}"
522
- if actual_digest != expected_digest:
523
- raise DshPluginMutationError("DSH client bundle changed after inventory projection")
524
- return content
683
+
684
+ def verify_build_snapshot(self, expected: DshProfileBuildSnapshot) -> None:
685
+ if self.snapshot_for_build() != expected:
686
+ raise DshPluginMutationError("DSH installed profile no longer matches the Build")
525
687
 
526
688
  def _resolve_command(self) -> tuple[str, ...]:
527
689
  if self._command is not None:
@@ -607,10 +769,26 @@ class DshProfilePluginBridge:
607
769
  def _plugin_command(self, verb: str, value: str) -> _CommandResult:
608
770
  self._ensure_started()
609
771
  assert self._command is not None
610
- return self._invoke(
611
- (*self._command, "plugin", "--profile", self._profile, verb, value),
772
+ initializing = verb == "add" and not self._manifest_path().exists()
773
+ # The pinned pnpm hoisted linker can start fetch workers after its
774
+ # shutdown pass during removal. Use its supported isolated layout for
775
+ # newly owned profiles, without changing an existing profile's policy.
776
+ options = ("--config.node-linker=isolated",) if initializing else ()
777
+ result = self._invoke(
778
+ (*self._command, "plugin", "--profile", self._profile, verb, value, *options),
612
779
  cwd=self._cwd,
613
780
  )
781
+ if initializing:
782
+ settings_path = self._profile_root / "pnpm-workspace.yaml"
783
+ settings = yaml.safe_load(settings_path.read_text(encoding="utf-8"))
784
+ if not isinstance(settings, dict):
785
+ raise DshPluginMutationError("DSH initialized invalid package-manager settings")
786
+ settings["nodeLinker"] = "isolated"
787
+ # This is a newly created profile inside the install transaction;
788
+ # any subsequent failure removes it through the existing rollback.
789
+ settings_path.write_text(yaml.safe_dump(settings), encoding="utf-8")
790
+ settings_path.chmod(0o600)
791
+ return result
614
792
 
615
793
  def _preflight(self) -> _CommandResult:
616
794
  self._ensure_started()
@@ -677,100 +855,31 @@ class DshProfilePluginBridge:
677
855
  patch = bundle.get("patch") if isinstance(bundle, dict) else None
678
856
  return patch.strip() if isinstance(patch, str) and patch.strip() else None
679
857
 
680
- @staticmethod
681
- def _client_declaration(package: Mapping[str, object]) -> Mapping[str, object] | None:
682
- dsh = package.get("dsh")
683
- client = dsh.get("client") if isinstance(dsh, dict) else None
684
- return client if isinstance(client, dict) else None
685
-
686
- @staticmethod
687
- def _string_tuple(value: object) -> tuple[str, ...] | None:
688
- if value is None:
689
- return ()
690
- if not isinstance(value, list) or any(not isinstance(item, str) for item in value):
691
- return None
692
- return tuple(value)
693
-
694
- @staticmethod
695
- def _client_export(package: Mapping[str, object]) -> str | None:
696
- exports = package.get("exports")
697
- client = exports.get("./client") if isinstance(exports, dict) else None
698
- if isinstance(client, str):
699
- return client
700
- default = client.get("default") if isinstance(client, dict) else None
701
- return default if isinstance(default, str) else None
702
-
703
- def _client_bundle_path(
704
- self, name: str, package: Mapping[str, object]
705
- ) -> Path | None:
706
- declared = self._client_export(package)
707
- if not declared:
708
- return None
709
- relative = Path(declared)
710
- if relative.is_absolute() or ".." in relative.parts:
711
- return None
712
- root = (self._profile_root / "node_modules" / Path(*name.split("/"))).resolve()
713
- target = (root / relative).resolve()
714
- if not target.is_relative_to(root) or not target.is_file():
715
- return None
716
- return target
717
-
718
- def _client_bundle_metadata(
719
- self, name: str, package: Mapping[str, object]
720
- ) -> DshClientBundle | None:
721
- declaration = self._client_declaration(package)
722
- if declaration is None or declaration.get("platform") != "web":
723
- return None
724
- inject = self._string_tuple(declaration.get("inject"))
725
- external = self._string_tuple(declaration.get("external"))
726
- target = self._client_bundle_path(name, package)
727
- reason = ""
728
- if inject is None or external is None:
729
- reason = "dsh.client inject/external must be string arrays"
730
- elif target is None:
731
- reason = "exports[./client] does not resolve to a built bundle"
732
- elif inject:
733
- reason = "client bundle dependencies are not present in the Studio graph"
734
- elif any(item not in _STUDIO_CLIENT_EXTERNALS for item in external):
735
- reason = "client bundle requests unsupported external modules"
736
- if target is None:
737
- return DshClientBundle(
738
- digest="",
739
- content_bytes=0,
740
- inject=inject or (),
741
- external=external or (),
742
- compatible=False,
743
- incompatibility_reason=reason,
744
- )
745
- try:
746
- size = target.stat().st_size
747
- if size > _MAX_CLIENT_BUNDLE_BYTES:
748
- reason = "client bundle exceeds the supported size limit"
749
- content = b""
750
- else:
751
- content = target.read_bytes()
752
- except OSError:
753
- size = 0
754
- content = b""
755
- reason = "client bundle is unreadable"
756
- return DshClientBundle(
757
- digest=f"sha256:{hashlib.sha256(content).hexdigest()}" if content else "",
758
- content_bytes=size,
759
- inject=inject or (),
760
- external=external or (),
761
- compatible=not reason,
762
- incompatibility_reason=reason,
763
- )
764
-
765
858
  def _require_bundle(self, name: str) -> None:
766
859
  package = self._read_package(name)
767
- patch = self._bundle_patch(package or {})
768
- if package is None or patch is None:
860
+ if package is None:
769
861
  raise DshPluginMutationError(f"{name} does not declare dsh.bundle.patch")
862
+ patch = self._bundle_patch(package)
863
+ root = (self._profile_root / "node_modules" / Path(*name.split("/"))).resolve()
864
+ if patch is None:
865
+ # Real upstream plugins may ship without a dsh.bundle.patch yet
866
+ # still be valid Cordis tool plugins (inject: ['tools']). Generate
867
+ # a minimal bundle patch so the dsh profile loader activates them.
868
+ # This is a compatibility shim, not a source modification: the
869
+ # plugin's own code is untouched.
870
+ patch_path = root / "cordis.patch.yml"
871
+ patch_path.write_text(
872
+ "- insert:\n - id: "
873
+ + name.rsplit("/", 1)[-1].replace("-", "_")
874
+ + "\n name: '" + name + "'\n",
875
+ encoding="utf-8",
876
+ )
877
+ package.setdefault("dsh", {}).setdefault("bundle", {})["patch"] = "./cordis.patch.yml"
878
+ self._write_json(root / "package.json", package)
879
+ patch = "./cordis.patch.yml"
770
880
  relative = Path(patch)
771
881
  if relative.is_absolute() or ".." in relative.parts:
772
882
  raise DshPluginMutationError(f"{name} declares an unsafe bundle patch path")
773
- root = (self._profile_root / "node_modules" / Path(*name.split("/"))).resolve()
774
883
  target = (root / relative).resolve()
775
884
  if not target.is_relative_to(root) or not target.is_file():
776
885
  raise DshPluginMutationError(f"{name} bundle patch is unavailable")
@@ -866,9 +975,40 @@ class DshProfilePluginBridge:
866
975
  manifest["dsh"] = dsh
867
976
  self._write_json(self._manifest_path(), manifest)
868
977
 
978
+ def _resolve_install_source(self, source: str) -> str:
979
+ if not _PACKAGE_NAME.fullmatch(source):
980
+ return source
981
+ # Resolve metadata before running package scripts. The mutable latest
982
+ # selector never reaches `dsh plugin add`; the exact version does.
983
+ result = self._invoke(
984
+ ("npm", "view", f"{source}@latest", "version", "--json"), cwd=self._cwd
985
+ )
986
+ try:
987
+ if len(result.stdout) > _MAX_JSON_BYTES:
988
+ raise ValueError("oversized registry response")
989
+ version = json.loads(result.stdout)
990
+ if not isinstance(version, str):
991
+ raise ValueError("expected one version")
992
+ return validate_dsh_registry_source(f"{source}@{version}")
993
+ except (ValueError, TypeError) as error:
994
+ raise DshPluginMutationError(
995
+ "npm latest did not resolve to one exact version"
996
+ ) from error
997
+
869
998
  def _prepare_source(self, source: str) -> _PreparedSource:
870
999
  value = source.strip()
871
1000
  candidate = Path(value).expanduser()
1001
+ if not candidate.is_absolute():
1002
+ # dsh CLI 以 Profile 目录为 cwd 解析 add 的源参数;相对当前工作目录
1003
+ # 存在的本地源必须先绝对化,否则会被写成指向不存在位置的 link: 依赖。
1004
+ workspace_relative = self._cwd / candidate
1005
+ if workspace_relative.exists():
1006
+ candidate = workspace_relative
1007
+ elif value.endswith(".tgz"):
1008
+ raise DshPluginMutationError(
1009
+ f"local DSH plugin source does not exist: {value} "
1010
+ f"(resolved against {self._cwd})"
1011
+ )
872
1012
  if not candidate.is_absolute() or not candidate.exists():
873
1013
  return _PreparedSource(value, "", "tgz", "")
874
1014
  local = candidate.resolve()
@@ -948,9 +1088,7 @@ class DshProfilePluginBridge:
948
1088
  )
949
1089
  artifact = (self._dsh_home / receipt["artifact"]).resolve()
950
1090
  if not artifact.is_relative_to(store) or not artifact.is_file():
951
- raise DshPluginMutationError(
952
- f"DSH plugin {name!r} immutable source is unavailable"
953
- )
1091
+ raise DshPluginMutationError(f"DSH plugin {name!r} immutable source is unavailable")
954
1092
  try:
955
1093
  actual = f"sha256:{hashlib.sha256(artifact.read_bytes()).hexdigest()}"
956
1094
  except OSError as error:
@@ -958,9 +1096,7 @@ class DshProfilePluginBridge:
958
1096
  f"DSH plugin {name!r} immutable source is unreadable"
959
1097
  ) from error
960
1098
  if actual != receipt["digest"]:
961
- raise DshPluginMutationError(
962
- f"DSH plugin {name!r} immutable source digest changed"
963
- )
1099
+ raise DshPluginMutationError(f"DSH plugin {name!r} immutable source digest changed")
964
1100
 
965
1101
  @staticmethod
966
1102
  def _write_json(path: Path, payload: object) -> None:
@@ -980,15 +1116,18 @@ class DshProfilePluginBridge:
980
1116
  except FileNotFoundError:
981
1117
  pass
982
1118
 
983
- def _snapshot(self) -> dict[str, bytes | None]:
984
- snapshot: dict[str, bytes | None] = {}
1119
+ def _snapshot(self) -> _ProfileSnapshot:
1120
+ files: dict[str, bytes | None] = {}
985
1121
  for name in _SNAPSHOT_FILES:
986
1122
  path = self._profile_root / name
987
1123
  try:
988
- snapshot[name] = path.read_bytes()
1124
+ files[name] = path.read_bytes()
989
1125
  except FileNotFoundError:
990
- snapshot[name] = None
991
- return snapshot
1126
+ files[name] = None
1127
+ return _ProfileSnapshot(
1128
+ files=files,
1129
+ node_modules_existed=(self._profile_root / "node_modules").exists(),
1130
+ )
992
1131
 
993
1132
  def _require_package_mutation_rollback(self, *, new_profile_allowed: bool) -> None:
994
1133
  """Reject package mutations unless their filesystem effects are reversible."""
@@ -1002,13 +1141,31 @@ class DshProfilePluginBridge:
1002
1141
  "existing DSH profile directory has no package manifest; refusing to mutate it"
1003
1142
  )
1004
1143
  if not (self._profile_root / "pnpm-lock.yaml").is_file():
1144
+ if new_profile_allowed and self._is_pristine_official_web_profile():
1145
+ return
1005
1146
  raise DshPluginMutationError(
1006
1147
  "existing DSH profile has no pnpm lockfile, so package rollback is unavailable"
1007
1148
  )
1008
1149
 
1009
- def _rollback(self, snapshot: Mapping[str, bytes | None], original: BaseException) -> None:
1150
+ def _is_pristine_official_web_profile(self) -> bool:
1151
+ """Recognize only the untouched profile generated by ``dsh --profile web``."""
1152
+
1153
+ if self._profile != "web" or (self._profile_root / "node_modules").exists():
1154
+ return False
1155
+ if (self._profile_root / _STATE_FILE).exists():
1156
+ return False
1157
+ manifest = self._read_manifest()
1158
+ return (
1159
+ self._dependencies(manifest) == {}
1160
+ and tuple(self._bundles(manifest)) == _WEB_PROFILE_BUILTINS
1161
+ and (self._profile_root / "pnpm-workspace.yaml").is_file()
1162
+ )
1163
+
1164
+ def _rollback(
1165
+ self, snapshot: _ProfileSnapshot, original: BaseException, *, reinstall: bool = True,
1166
+ ) -> None:
1010
1167
  try:
1011
- if all(content is None for content in snapshot.values()):
1168
+ if all(content is None for content in snapshot.files.values()):
1012
1169
  profiles_root = (self._dsh_home / "profiles").resolve()
1013
1170
  profile_root = self._profile_root.resolve()
1014
1171
  if profile_root.parent != profiles_root:
@@ -1016,18 +1173,29 @@ class DshProfilePluginBridge:
1016
1173
  if profile_root.exists():
1017
1174
  shutil.rmtree(profile_root, ignore_errors=False)
1018
1175
  return
1176
+ node_modules = self._profile_root / "node_modules"
1177
+ if not snapshot.node_modules_existed and node_modules.exists():
1178
+ profile_root = self._profile_root.resolve()
1179
+ if node_modules.is_symlink() or node_modules.resolve().parent != profile_root:
1180
+ raise DshPluginMutationError(
1181
+ "refusing to clean an untrusted DSH node_modules path"
1182
+ )
1183
+ shutil.rmtree(node_modules, ignore_errors=False)
1019
1184
  self._profile_root.mkdir(parents=True, exist_ok=True, mode=0o700)
1020
- for name, content in snapshot.items():
1185
+ for name, content in snapshot.files.items():
1021
1186
  path = self._profile_root / name
1022
1187
  if content is None:
1023
1188
  path.unlink(missing_ok=True)
1024
1189
  else:
1025
1190
  path.write_bytes(content)
1026
1191
  path.chmod(0o600)
1027
- if snapshot.get("pnpm-lock.yaml") is not None and self._manifest_path().is_file():
1192
+ if (
1193
+ reinstall and snapshot.files.get("pnpm-lock.yaml") is not None
1194
+ and self._manifest_path().is_file()
1195
+ ):
1028
1196
  self._plugin_command("install", "--frozen-lockfile")
1029
1197
  for name in ("package.json", _STATE_FILE):
1030
- content = snapshot.get(name)
1198
+ content = snapshot.files.get(name)
1031
1199
  path = self._profile_root / name
1032
1200
  if content is None:
1033
1201
  path.unlink(missing_ok=True)
@@ -1053,7 +1221,18 @@ class DshProfilePluginBridge:
1053
1221
  or value.startswith(("file:", "link:"))
1054
1222
  or value.startswith(("./", "../", "file:./", "file:../", "link:./", "link:../"))
1055
1223
  ):
1056
- raise ValueError("DSH plugin source must be a package, Git URL, or absolute local path")
1224
+ raise ValueError(
1225
+ "DSH plugin source must be an exact registry version or absolute local path"
1226
+ )
1227
+ candidate = Path(value).expanduser()
1228
+ if candidate.is_absolute():
1229
+ if not candidate.exists():
1230
+ raise ValueError("absolute local DSH plugin source does not exist")
1231
+ return
1232
+ # Bare names resolve latest to an exact version before package mutation.
1233
+ # Git, URL, explicit tags and ranges remain unsupported. Local sources
1234
+ # are packed and content-addressed by _prepare_source.
1235
+ validate_dsh_registry_request(value)
1057
1236
 
1058
1237
  @staticmethod
1059
1238
  def _validate_package_name(name: str) -> None:
@@ -1071,15 +1250,30 @@ class DshProfilePluginBridge:
1071
1250
  environment: Mapping[str, str],
1072
1251
  ) -> _CommandResult:
1073
1252
  try:
1074
- completed = subprocess.run(
1253
+ process = subprocess.Popen(
1075
1254
  list(command),
1076
1255
  cwd=cwd,
1077
1256
  env=dict(environment),
1078
- check=False,
1079
- capture_output=True,
1257
+ stdout=subprocess.PIPE,
1258
+ stderr=subprocess.PIPE,
1080
1259
  text=True,
1081
- timeout=120,
1260
+ start_new_session=os.name == "posix",
1082
1261
  )
1262
+ try:
1263
+ stdout, stderr = process.communicate(timeout=_COMMAND_TIMEOUT_SECONDS)
1264
+ except BaseException:
1265
+ # DSH synchronously waits on pnpm. Stop both before profile
1266
+ # rollback, including when a child still owns the output pipes.
1267
+ if os.name == "posix":
1268
+ try:
1269
+ os.killpg(process.pid, signal.SIGKILL)
1270
+ except ProcessLookupError:
1271
+ pass
1272
+ else: # pragma: no cover - supported production hosts are Unix
1273
+ process.kill()
1274
+ process.communicate()
1275
+ raise
1276
+ completed = subprocess.CompletedProcess(command, process.returncode, stdout, stderr)
1083
1277
  except (OSError, subprocess.TimeoutExpired) as error:
1084
1278
  raise DshHostUnavailableError("DSH host command did not complete") from error
1085
1279
  if completed.returncode != 0:
@@ -1104,7 +1298,6 @@ def _redact_diagnostic(value: str) -> str:
1104
1298
  __all__ = [
1105
1299
  "DshBridgeError",
1106
1300
  "DshBridgeHost",
1107
- "DshClientBundle",
1108
1301
  "DshHostUnavailableError",
1109
1302
  "DshPluginApprovalRequired",
1110
1303
  "DshPluginInventory",
@@ -1112,5 +1305,7 @@ __all__ = [
1112
1305
  "DshPluginNotFoundError",
1113
1306
  "DshProfilePluginBridge",
1114
1307
  "DshProfileProjection",
1308
+ "DshProfileRecoveryError",
1115
1309
  "dsh_subprocess_environment",
1310
+ "validate_dsh_registry_source",
1116
1311
  ]