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.
- {agentengine_sdk_python-0.8.3.dist-info → agentengine_sdk_python-0.8.5.dist-info}/METADATA +13 -9
- agentengine_sdk_python-0.8.5.dist-info/RECORD +1296 -0
- ksadk/_build_provenance.json +1 -1
- ksadk/_process.py +83 -0
- ksadk/_str_enum.py +17 -0
- ksadk/a2ui/core.py +3 -3
- ksadk/api/client.py +111 -91
- ksadk/builders/code_builder.py +74 -21
- ksadk/builders/framework_requirements.py +22 -3
- ksadk/builders/ks3_uploader.py +36 -7
- ksadk/builders/managed_runtime_builder.py +1 -0
- ksadk/cli/__init__.py +68 -3
- ksadk/cli/cmd_config.py +4 -4
- ksadk/cli/cmd_create.py +12 -12
- ksadk/cli/cmd_hermes.py +24 -2
- ksadk/cli/cmd_invoke.py +28 -5
- ksadk/cli/cmd_managed_runtime.py +13 -1
- ksadk/cli/cmd_openclaw.py +26 -1
- ksadk/cli/cmd_plugin.py +76 -13
- ksadk/cli/cmd_studio.py +64 -16
- ksadk/cli/cmd_web.py +2 -5
- ksadk/cli/openclaw_env.py +1 -1
- ksadk/cli/resource_common.py +26 -0
- ksadk/cli/runtime_bootstrap.py +23 -1
- ksadk/codex/client.py +425 -96
- ksadk/codex/projection.py +24 -0
- ksadk/codex/runtime.py +56 -298
- ksadk/common/auth.py +16 -10
- ksadk/configs/__init__.py +1 -1
- ksadk/configs/env_registry.py +153 -31
- ksadk/configs/env_registry_studio.py +86 -0
- ksadk/configs/global_config.py +1 -1
- ksadk/configs/settings.py +3 -3
- ksadk/connector/__init__.py +17 -0
- ksadk/connector/channel.py +477 -0
- ksadk/context_engine/capabilities.py +52 -4
- ksadk/conversations/attachment_storage.py +10 -3
- ksadk/conversations/attachments.py +16 -5
- ksadk/conversations/message_projection.py +133 -21
- ksadk/conversations/normalize.py +28 -10
- ksadk/conversations/projector.py +4 -6
- ksadk/conversations/runtime_baseline.py +37 -0
- ksadk/conversations/runtime_invocation.py +22 -4
- ksadk/conversations/runtime_observability.py +39 -10
- ksadk/conversations/runtime_payloads.py +9 -0
- ksadk/conversations/runtime_persistence.py +105 -9
- ksadk/conversations/runtime_preparation.py +42 -33
- ksadk/conversations/runtime_resume.py +50 -2
- ksadk/conversations/runtime_stream_events.py +34 -35
- ksadk/conversations/runtime_streaming.py +33 -7
- ksadk/deployment/env_forward.py +2 -2
- ksadk/deployment/providers/serverless.py +1 -1
- ksadk/events/adapters/_codex_items.py +16 -8
- ksadk/events/adapters/_codex_validators.py +18 -7
- ksadk/events/adapters/_langgraph_support.py +34 -0
- ksadk/events/adapters/langgraph.py +15 -1
- ksadk/events/canonical_store.py +3 -1
- ksadk/events/projections.py +1 -1
- ksadk/events/session_event.py +2 -2
- ksadk/harness/__init__.py +14 -0
- ksadk/harness/app.py +11 -0
- ksadk/harness/artifact_store.py +213 -0
- ksadk/harness/capabilities.py +115 -0
- ksadk/harness/capability_runtime.py +237 -0
- ksadk/harness/closure_contract.py +92 -0
- ksadk/harness/compaction_record.py +122 -0
- ksadk/harness/compiler.py +284 -0
- ksadk/harness/config.py +2 -2
- ksadk/harness/conformance/__init__.py +33 -0
- ksadk/harness/conformance/contract.py +506 -0
- ksadk/harness/conformance/fixtures.py +102 -0
- ksadk/harness/context_engine.py +403 -0
- ksadk/harness/context_maintenance.py +259 -0
- ksadk/harness/context_manifest.py +188 -0
- ksadk/harness/context_tuning.py +168 -0
- ksadk/harness/draft_runtime.py +322 -0
- ksadk/harness/engine/__init__.py +40 -0
- ksadk/harness/engine/base.py +122 -0
- ksadk/harness/engine/budgets.py +108 -0
- ksadk/harness/engine/capability_declarations.py +59 -0
- ksadk/harness/engine/completion_guard.py +42 -0
- ksadk/harness/engine/context_pipeline.py +579 -0
- ksadk/harness/engine/graph_builder.py +859 -0
- ksadk/harness/engine/langgraph.py +931 -0
- ksadk/harness/engine/mcp_disclosure.py +750 -0
- ksadk/harness/engine/policy_runtime.py +70 -0
- ksadk/harness/engine/postgres_checkpointer.py +63 -0
- ksadk/harness/engine/run_control_bridge.py +80 -0
- ksadk/harness/engine/run_types.py +88 -0
- ksadk/harness/engine/skill_disclosure.py +189 -0
- ksadk/harness/engine/spans.py +41 -0
- ksadk/harness/engine/subagents.py +343 -0
- ksadk/harness/engine/thread_ids.py +112 -0
- ksadk/harness/engine/tool_dispatch.py +144 -0
- ksadk/harness/event_tree.py +212 -0
- ksadk/harness/events.py +461 -0
- ksadk/harness/execution_policy.py +94 -0
- ksadk/harness/insights.py +210 -0
- ksadk/harness/lifecycle.py +578 -0
- ksadk/harness/lifecycle_conformance.py +233 -0
- ksadk/harness/lifecycle_local_plane.py +104 -0
- ksadk/harness/loop/__init__.py +44 -0
- ksadk/harness/loop/reason.py +454 -0
- ksadk/harness/loop/tools.py +762 -0
- ksadk/harness/managed_runtime.py +884 -0
- ksadk/harness/mcp_matrix.py +474 -0
- ksadk/harness/mcp_runtime.py +471 -0
- ksadk/harness/memory_consolidation.py +157 -0
- ksadk/harness/memory_loop.py +156 -0
- ksadk/harness/memory_runtime.py +541 -0
- ksadk/harness/metrics.py +90 -0
- ksadk/harness/model_capability.py +253 -0
- ksadk/harness/model_provider.py +220 -0
- ksadk/harness/observability/__init__.py +29 -0
- ksadk/harness/observability/inspection.py +528 -0
- ksadk/harness/observability/metrics.py +167 -0
- ksadk/harness/observability/otel_exporter.py +218 -0
- ksadk/harness/prompt_cache.py +70 -0
- ksadk/harness/public_activity.py +154 -0
- ksadk/harness/readiness.py +249 -0
- ksadk/harness/reasoner.py +466 -13
- ksadk/harness/release_readiness.py +140 -0
- ksadk/harness/resource_ref.py +69 -0
- ksadk/harness/run_control.py +668 -0
- ksadk/harness/runtime.py +171 -136
- ksadk/harness/runtime_capabilities.py +158 -0
- ksadk/harness/runtime_server.py +514 -0
- ksadk/harness/sandbox_adapters.py +739 -0
- ksadk/harness/sandbox_backend.py +530 -0
- ksadk/harness/sandbox_conformance.py +288 -0
- ksadk/harness/sandbox_lease.py +77 -0
- ksadk/harness/sandbox_lease_conformance.py +259 -0
- ksadk/harness/sandbox_matrix.py +170 -0
- ksadk/harness/sandbox_reconciliation.py +140 -0
- ksadk/harness/sandbox_recovery.py +586 -0
- ksadk/harness/sandbox_recovery_conformance.py +246 -0
- ksadk/harness/session_store.py +243 -0
- ksadk/harness/skill_composition.py +212 -0
- ksadk/harness/skill_feedback.py +123 -0
- ksadk/harness/skill_matrix.py +233 -0
- ksadk/harness/skill_runtime.py +259 -0
- ksadk/harness/spec.py +332 -0
- ksadk/harness/state.py +153 -0
- ksadk/harness/strategies.py +149 -0
- ksadk/harness/subagent.py +253 -0
- ksadk/harness/tool_policy.py +131 -0
- ksadk/harness/tool_receipts.py +96 -0
- ksadk/harness/tool_reliability.py +81 -0
- ksadk/harness/tools.py +38 -13
- ksadk/harness/user_errors.py +69 -0
- ksadk/harness/working_context.py +81 -0
- ksadk/harness/working_context_patch.py +99 -0
- ksadk/identity/resolver.py +18 -0
- ksadk/interaction/__init__.py +1 -1
- ksadk/interaction/ledger.py +1 -1
- ksadk/interaction/provider.py +2 -1
- ksadk/interaction/providers/__init__.py +3 -1
- ksadk/interaction/providers/adk.py +1 -1
- ksadk/interaction/providers/codex.py +1 -1
- ksadk/interaction/providers/harness.py +50 -0
- ksadk/interaction/providers/langgraph.py +2 -1
- ksadk/kernel/__init__.py +10 -0
- ksadk/kernel/approval_presentation.py +91 -0
- ksadk/kernel/authorization.py +1 -1
- ksadk/kernel/bootstrap.py +114 -106
- ksadk/kernel/contract_fingerprints.py +1 -1
- ksadk/kernel/contracts.py +5 -0
- ksadk/kernel/control.py +32 -1
- ksadk/kernel/execution_grants.py +221 -0
- ksadk/kernel/execution_grants_memory.py +133 -0
- ksadk/kernel/execution_grants_postgres.py +217 -0
- ksadk/kernel/execution_grants_sqlite.py +209 -0
- ksadk/kernel/ingress.py +42 -29
- ksadk/kernel/mapping.py +1 -1
- ksadk/kernel/memory_store.py +24 -11
- ksadk/kernel/postgres_store.py +63 -33
- ksadk/kernel/recovery.py +78 -14
- ksadk/kernel/sql/001_agent_kernel.sql +23 -0
- ksadk/kernel/sqlite_store.py +331 -25
- ksadk/kernel/state.py +2 -3
- ksadk/kernel/store.py +72 -2
- ksadk/kernel/worker.py +144 -51
- ksadk/kernel/worker_history.py +72 -0
- ksadk/kernel/worker_identity.py +47 -0
- ksadk/knowledge_base/client.py +57 -27
- ksadk/mcp_runtime/__init__.py +12 -4
- ksadk/memory/adk/backends/sdk_ltm_backend.py +161 -41
- ksadk/memory/adk/resilient_session_service.py +25 -17
- ksadk/memory/adk/short_term_memory.py +26 -9
- ksadk/memory/coordinator.py +22 -6
- ksadk/memory/extraction.py +40 -1
- ksadk/memory/models.py +84 -5
- ksadk/memory/provider_resolver.py +8 -0
- ksadk/memory/providers/local_postgres.py +200 -0
- ksadk/memory/providers/local_sqlite.py +42 -3
- ksadk/memory/retrieval.py +189 -0
- ksadk/memory/store_adapter.py +150 -0
- ksadk/model_policy.py +2 -2
- ksadk/model_proxy/detect.py +254 -0
- ksadk/model_proxy/server.py +14 -7
- ksadk/model_proxy/transform.py +40 -2
- ksadk/plugins/__init__.py +5 -1
- ksadk/plugins/artifacts.py +275 -0
- ksadk/plugins/bridges/__init__.py +4 -2
- ksadk/plugins/bridges/codex.py +120 -13
- ksadk/plugins/bridges/dsh.py +351 -156
- ksadk/plugins/builtins.py +72 -35
- ksadk/plugins/bundle_security.py +28 -4
- ksadk/plugins/codex_manifest.py +983 -0
- ksadk/plugins/companion_artifacts.py +83 -0
- ksadk/plugins/companions.py +368 -0
- ksadk/plugins/composition.py +8 -0
- ksadk/plugins/contracts.py +119 -1
- ksadk/plugins/delivery.py +153 -0
- ksadk/plugins/dsh_channels.py +156 -0
- ksadk/plugins/dsh_home.py +140 -0
- ksadk/plugins/dsh_installation_digest.py +71 -0
- ksadk/plugins/dsh_teams.py +105 -0
- ksadk/plugins/dsh_toolchain.py +203 -32
- ksadk/plugins/ecosystem_probe.py +6 -6
- ksadk/plugins/execution_host.py +99 -0
- ksadk/plugins/host.py +102 -39
- ksadk/plugins/kernel_adapter.py +205 -0
- ksadk/plugins/providers/__init__.py +23 -0
- ksadk/plugins/providers/bundles/dsh-channels-client/client.js +19 -0
- ksadk/plugins/providers/bundles/dsh-channels-client/cordis.patch.yml +3 -0
- ksadk/plugins/providers/bundles/dsh-channels-client/index.mjs +7 -0
- ksadk/plugins/providers/bundles/dsh-channels-client/package.json +12 -0
- ksadk/plugins/providers/bundles/dsh-knowledge/cordis.patch.yml +3 -0
- ksadk/plugins/providers/bundles/dsh-knowledge/index.mjs +53 -0
- ksadk/plugins/providers/bundles/dsh-knowledge/package.json +16 -0
- ksadk/plugins/providers/bundles/dsh-memory/cordis.patch.yml +3 -0
- ksadk/plugins/providers/bundles/dsh-memory/index.mjs +175 -0
- ksadk/plugins/providers/bundles/dsh-memory/package.json +16 -0
- ksadk/plugins/providers/bundles/dsh-platform-resources/client.mjs +166 -0
- ksadk/plugins/providers/bundles/dsh-platform-resources/cordis.patch.yml +3 -0
- ksadk/plugins/providers/bundles/dsh-platform-resources/index.mjs +9 -0
- ksadk/plugins/providers/bundles/dsh-platform-resources/package.json +12 -0
- ksadk/plugins/providers/bundles/dsh-skill-center/cordis.patch.yml +3 -0
- ksadk/plugins/providers/bundles/dsh-skill-center/index.mjs +125 -0
- ksadk/plugins/providers/bundles/dsh-skill-center/package.json +16 -0
- ksadk/plugins/providers/bundles/dsh-teams-client/client.js +29 -0
- ksadk/plugins/providers/bundles/dsh-teams-client/cordis.patch.yml +3 -0
- ksadk/plugins/providers/bundles/dsh-teams-client/index.mjs +11 -0
- ksadk/plugins/providers/bundles/dsh-teams-client/package.json +35 -0
- ksadk/plugins/providers/bundles/dsh-teams-leader-policy/cordis.patch.yml +3 -0
- ksadk/plugins/providers/bundles/dsh-teams-leader-policy/index.mjs +13 -0
- ksadk/plugins/providers/bundles/dsh-teams-leader-policy/package.json +27 -0
- ksadk/plugins/providers/bundles/dsh-teams-profile/cordis.patch.yml +3 -0
- ksadk/plugins/providers/bundles/dsh-teams-profile/index.mjs +12 -0
- ksadk/plugins/providers/bundles/dsh-teams-profile/package.json +26 -0
- ksadk/plugins/providers/bundles/dsh-teams-router/cordis.patch.yml +3 -0
- ksadk/plugins/providers/bundles/dsh-teams-router/index.mjs +16 -0
- ksadk/plugins/providers/bundles/dsh-teams-router/package.json +27 -0
- ksadk/plugins/providers/bundles/dsh-teams-tasks/cordis.patch.yml +3 -0
- ksadk/plugins/providers/bundles/dsh-teams-tasks/index.mjs +16 -0
- ksadk/plugins/providers/bundles/dsh-teams-tasks/package.json +27 -0
- ksadk/plugins/providers/bundles/dsh-teams-tools/cordis.patch.yml +3 -0
- ksadk/plugins/providers/bundles/dsh-teams-tools/index.mjs +25 -0
- ksadk/plugins/providers/bundles/dsh-teams-tools/package.json +29 -0
- ksadk/plugins/providers/bundles/dsh-teams-tools/tools.json +235 -0
- ksadk/plugins/providers/bundles/ksadk-codex/index.mjs +1 -1
- ksadk/plugins/providers/bundles/ksadk-dsh-capability-host/cordis.patch.yml +3 -0
- ksadk/plugins/providers/bundles/ksadk-dsh-capability-host/index.mjs +920 -0
- ksadk/plugins/providers/bundles/ksadk-dsh-capability-host/package.json +27 -0
- ksadk/plugins/providers/bundles/ksadk-dsh-companion-host/client.mjs +133 -0
- ksadk/plugins/providers/bundles/ksadk-dsh-companion-host/index.mjs +14 -0
- ksadk/plugins/providers/bundles/ksadk-dsh-studio/client.js +115 -0
- ksadk/plugins/providers/bundles/ksadk-dsh-studio/index.mjs +19 -0
- ksadk/plugins/providers/bundles/ksadk-dsh-studio/package.json +7 -0
- ksadk/plugins/providers/bundles/ksadk-harness/index.mjs +1 -1
- ksadk/plugins/providers/codex.py +344 -70
- ksadk/plugins/providers/codex_native.py +368 -0
- ksadk/plugins/providers/codex_turn.py +301 -0
- ksadk/plugins/providers/dsh_capabilities.py +1450 -0
- ksadk/plugins/providers/dsh_mcp.py +467 -0
- ksadk/plugins/providers/harness.py +294 -141
- ksadk/plugins/providers/harness_delegation.py +593 -0
- ksadk/plugins/providers/harness_managed.py +456 -0
- ksadk/plugins/providers/harness_tools.py +326 -0
- ksadk/plugins/providers/mcp_projection.py +150 -0
- ksadk/plugins/providers/platform_resources.py +657 -0
- ksadk/plugins/providers/shipped_dsh.py +4 -1
- ksadk/plugins/resolver.py +8 -1
- ksadk/plugins/subagent_providers/codex.py +28 -6
- ksadk/plugins/teams/__init__.py +5 -0
- ksadk/plugins/teams/api.py +405 -0
- ksadk/plugins/teams/application.py +680 -0
- ksadk/plugins/teams/artifacts.py +60 -0
- ksadk/plugins/teams/context.py +96 -0
- ksadk/plugins/teams/contracts.py +136 -0
- ksadk/plugins/teams/domain.py +1427 -0
- ksadk/plugins/teams/domain_values.py +24 -0
- ksadk/plugins/teams/errors.py +13 -0
- ksadk/plugins/teams/migration.py +311 -0
- ksadk/plugins/teams/migration_cli.py +322 -0
- ksadk/plugins/teams/postgres_store.py +138 -0
- ksadk/plugins/teams/runtime.py +777 -0
- ksadk/plugins/teams/source_projection.py +84 -0
- ksadk/plugins/teams/store.py +311 -0
- ksadk/plugins/teams/task_decisions.py +423 -0
- ksadk/plugins/teams/transport.py +91 -0
- ksadk/plugins/teams/workspaces.py +255 -0
- ksadk/resource_runtime/__init__.py +4 -0
- ksadk/resource_runtime/broker.py +616 -0
- ksadk/resource_runtime/build_artifacts.py +165 -0
- ksadk/resource_runtime/contracts.py +138 -0
- ksadk/resource_runtime/discovery_receipts.py +100 -0
- ksadk/resource_runtime/discovery_skills.py +172 -0
- ksadk/resource_runtime/errors.py +6 -0
- ksadk/resource_runtime/ipc.py +142 -0
- ksadk/resource_runtime/kernel_approval.py +165 -0
- ksadk/resource_runtime/knowledge.py +114 -0
- ksadk/resource_runtime/langgraph.py +94 -0
- ksadk/resource_runtime/leases.py +191 -0
- ksadk/resource_runtime/managed_projection.py +186 -0
- ksadk/resource_runtime/mcp_server.py +138 -0
- ksadk/resource_runtime/memory_provider.py +318 -0
- ksadk/resource_runtime/memory_receipts.py +34 -0
- ksadk/resource_runtime/operation_ledger.py +211 -0
- ksadk/resource_runtime/plugin_config.py +48 -0
- ksadk/resource_runtime/policy_authorization.py +54 -0
- ksadk/resource_runtime/process.py +110 -0
- ksadk/resource_runtime/skill_artifacts.py +153 -0
- ksadk/resource_runtime/skill_runtime.py +42 -0
- ksadk/resource_runtime/skill_workspace.py +118 -0
- ksadk/resource_runtime/skills.py +215 -0
- ksadk/resource_runtime/snapshots.py +180 -0
- ksadk/resource_runtime/socket_server.py +103 -0
- ksadk/resource_runtime/supervisor.py +380 -0
- ksadk/resource_runtime/worker.py +504 -0
- ksadk/runners/_langgraph_runner_streams.py +182 -38
- ksadk/runners/_langgraph_state_reader.py +106 -0
- ksadk/runners/_session_identity.py +285 -0
- ksadk/runners/adk_runner.py +166 -63
- ksadk/runners/base_runner.py +29 -1
- ksadk/runners/langgraph_runner.py +542 -195
- ksadk/runners/patch_langchain.py +47 -0
- ksadk/runners/usage_accumulator.py +11 -3
- ksadk/runtime/_runner_adapter/stream_mapping.py +194 -20
- ksadk/runtime/adapter.py +2 -0
- ksadk/runtime/conversation_execution.py +117 -17
- ksadk/runtime/executor.py +87 -3
- ksadk/runtime/factory.py +65 -274
- ksadk/runtime/framework_adapters.py +23 -0
- ksadk/runtime/preprocessing.py +21 -5
- ksadk/runtime/runner_adapter.py +106 -18
- ksadk/runtime/skill_eval_result.py +36 -0
- ksadk/runtime/timing.py +46 -0
- ksadk/runtime/usage.py +49 -18
- ksadk/runtime_context.py +142 -2
- ksadk/sandbox/backends/e2b.py +288 -25
- ksadk/sandbox/backends/local_process.py +72 -39
- ksadk/sandbox/base.py +64 -1
- ksadk/sandbox/diagnostics.py +82 -0
- ksadk/sandbox/e2b_connection.py +66 -0
- ksadk/sandbox/local_controls.py +746 -0
- ksadk/sandbox/local_launcher.py +90 -0
- ksadk/scheduler/dispatcher.py +1 -1
- ksadk/scheduler/sqlite_store.py +18 -0
- ksadk/server/composition.py +2 -0
- ksadk/server/factory.py +5 -0
- ksadk/server/invocation_identity.py +118 -0
- ksadk/server/persistence_capability.py +516 -0
- ksadk/server/routes/common.py +32 -3
- ksadk/server/routes/control.py +271 -52
- ksadk/server/routes/dependencies.py +16 -0
- ksadk/server/routes/kernel_ingress.py +9 -1
- ksadk/server/routes/misc.py +233 -25
- ksadk/server/routes/models.py +47 -4
- ksadk/server/routes/openai_compat.py +76 -13
- ksadk/server/routes/projection.py +252 -12
- ksadk/server/routes/run.py +106 -35
- ksadk/server/routes/sessions.py +491 -138
- ksadk/server/routes/streaming.py +43 -9
- ksadk/server/routes/workspace.py +130 -21
- ksadk/server/static/assets/{ArtifactsPanel-10GPXIJ6.js → ArtifactsPanel-C1YoaKhx.js} +1 -1
- ksadk/server/static/assets/{CodeBlock-BXk1l-G1.js → CodeBlock-DHuAWfr5.js} +10 -7
- ksadk/server/static/assets/MathMessageMarkdown-cvdrBwYr.js +8 -0
- ksadk/server/static/assets/{MermaidBlock--OEYoXIJ.js → MermaidBlock-DeBkbJvI.js} +9 -9
- ksadk/server/static/assets/{NativeTerminalPanel-DrEe1syq.js → NativeTerminalPanel-oJj44_sV.js} +1 -1
- ksadk/server/static/assets/{abnfDiagram-VCTEODGH-eeTFJLvo.js → abnfDiagram-VCTEODGH-Dhfv25o2.js} +1 -1
- ksadk/server/static/assets/{arc-S3ZNbIAd.js → arc-C35w-pn1.js} +1 -1
- ksadk/server/static/assets/architecture-7GRP2DOG-BCsa26zi.js +1 -0
- ksadk/server/static/assets/{architectureDiagram-5GKGNRK7-CRjgEJDX.js → architectureDiagram-5GKGNRK7-FoMY_u6D.js} +1 -1
- ksadk/server/static/assets/{blockDiagram-NRAW4CY4-C7bsBoNH.js → blockDiagram-NRAW4CY4-zD4Rt7oE.js} +1 -1
- ksadk/server/static/assets/{c4Diagram-UCG6FXSJ-BX3c33Ob.js → c4Diagram-UCG6FXSJ-BCtGGkhN.js} +1 -1
- ksadk/server/static/assets/channel-_KDifNys.js +1 -0
- ksadk/server/static/assets/chunk-2Q5K7J3B-HdVkJ9F0.js +1 -0
- ksadk/server/static/assets/{chunk-5VM5RSS4-bZ7ws-8m.js → chunk-5VM5RSS4-C2Ms13_k.js} +1 -1
- ksadk/server/static/assets/{chunk-F27PBJKO-D2r0pvhY.js → chunk-F27PBJKO-BoTj-qgx.js} +1 -1
- ksadk/server/static/assets/{chunk-G27WJ6UU-CZwF3oUP.js → chunk-G27WJ6UU-DOMyyjHH.js} +1 -1
- ksadk/server/static/assets/{chunk-JWPE2WC7-DigFYCML.js → chunk-JWPE2WC7-DbJJqDli.js} +1 -1
- ksadk/server/static/assets/{chunk-LCL6LL3I-CnorGV5q.js → chunk-LCL6LL3I-CLuRCt_W.js} +1 -1
- ksadk/server/static/assets/{chunk-POPQ4Y6H-CexntQA-.js → chunk-POPQ4Y6H-D0Dz847e.js} +1 -1
- ksadk/server/static/assets/{chunk-RHFEMEQ7-CHTC7UxA.js → chunk-RHFEMEQ7-BWSRzEcd.js} +1 -1
- ksadk/server/static/assets/{chunk-SVP7TREG-BC1EWt_-.js → chunk-SVP7TREG-CPoHqkCY.js} +1 -1
- ksadk/server/static/assets/{chunk-XXDRQBXY-BTG24xN0.js → chunk-XXDRQBXY-Bx1jS-X-.js} +1 -1
- ksadk/server/static/assets/classDiagram-DTDB5LWJ-BVCW3ZqZ.js +1 -0
- ksadk/server/static/assets/classDiagram-v2-JRS7N3AN-BVCW3ZqZ.js +1 -0
- ksadk/server/static/assets/{cose-bilkent-JH36ORCC-CkonRdAS.js → cose-bilkent-JH36ORCC-LzyWm4m0.js} +1 -1
- ksadk/server/static/assets/cynefin-OW5HDTMX-CNXjFctL.js +1 -0
- ksadk/server/static/assets/{cynefinDiagram-5FMLGOSQ-DEOCBe15.js → cynefinDiagram-5FMLGOSQ-BlW_bHED.js} +1 -1
- ksadk/server/static/assets/{dagre-3AP2YEHR-DBThPuLa.js → dagre-3AP2YEHR-Bu_S86_i.js} +1 -1
- ksadk/server/static/assets/{dagre-ClTPrTht.js → dagre-B7nCawed.js} +1 -1
- ksadk/server/static/assets/{diagram-S7CK7UJ4-DsZHrnpJ.js → diagram-S7CK7UJ4-W_Zow2Km.js} +1 -1
- ksadk/server/static/assets/{diagram-UQ7AKVKN-BJR4nhVr.js → diagram-UQ7AKVKN-CLIKAHON.js} +1 -1
- ksadk/server/static/assets/{diagram-VSXAHHWV-xzcERX0a.js → diagram-VSXAHHWV-Dv6_QAlN.js} +1 -1
- ksadk/server/static/assets/{diagram-VX7I27RA-tn2xM1xu.js → diagram-VX7I27RA-BWAWRDtZ.js} +1 -1
- ksadk/server/static/assets/{diagram-Z3DM3KII-BZPGBkw5.js → diagram-Z3DM3KII-XA_rWMss.js} +1 -1
- ksadk/server/static/assets/dist-0X_j2Rcc.js +1 -0
- ksadk/server/static/assets/{dist-4zN1_tq5.js → dist-1nXCdsej.js} +1 -1
- ksadk/server/static/assets/{dist-M67Hwu0n.js → dist-2vL2sAdF.js} +1 -1
- ksadk/server/static/assets/{dist-BJLUwH6W.js → dist-B3bA05-e.js} +1 -1
- ksadk/server/static/assets/{dist-8VWI-2bZ.js → dist-BLpRy_Yx.js} +1 -1
- ksadk/server/static/assets/{dist-D1V3f1B4.js → dist-BO_UwatH.js} +1 -1
- ksadk/server/static/assets/{dist-Cq4OL6nO.js → dist-BdNFAney.js} +1 -1
- ksadk/server/static/assets/{dist-DfGFrb0v.js → dist-C0J5_3qM.js} +1 -1
- ksadk/server/static/assets/{dist-DyqV4fL4.js → dist-CGt_UGeO.js} +1 -1
- ksadk/server/static/assets/{dist-DjhwGUgZ.js → dist-CIRMvUM4.js} +1 -1
- ksadk/server/static/assets/{dist-B1oWRmrH.js → dist-CIpWk_5f.js} +1 -1
- ksadk/server/static/assets/{dist-CXpH_Rdd.js → dist-CPyieXEf.js} +1 -1
- ksadk/server/static/assets/{dist-Btyo4vX4.js → dist-D-92S-Fd.js} +1 -1
- ksadk/server/static/assets/{dist-u8tVH5Ak.js → dist-DNKuMzWc.js} +1 -1
- ksadk/server/static/assets/{dist-DQAB0qn_.js → dist-DWfdmz9s.js} +2 -2
- ksadk/server/static/assets/{dist-B-46q3Hk.js → dist-DppE_r-D.js} +1 -1
- ksadk/server/static/assets/{dist-C71CsujO.js → dist-DyG6Ya-z.js} +1 -1
- ksadk/server/static/assets/{dist-DJmCPRVB.js → dist-M-SrdhhZ.js} +1 -1
- ksadk/server/static/assets/{dist-Bbwqa8TG.js → dist-mqD5TZup.js} +1 -1
- ksadk/server/static/assets/{dist-BI1qQH4n.js → dist-n3n6t_FO.js} +1 -1
- ksadk/server/static/assets/{dist-DUwKa_l6.js → dist-qe8xjesy.js} +1 -1
- ksadk/server/static/assets/{dist-B0gdk4rl.js → dist-t6MzE0jU.js} +1 -1
- ksadk/server/static/assets/{ebnfDiagram-PWID7BFC-DVfMsLge.js → ebnfDiagram-PWID7BFC-BfCzF1Xi.js} +1 -1
- ksadk/server/static/assets/{erDiagram-SSCWMZ5O-CWEZSYaw.js → erDiagram-SSCWMZ5O-Dpz9_0zn.js} +1 -1
- ksadk/server/static/assets/eventmodeling-NTZA5JFV-B5-8RH9c.js +1 -0
- ksadk/server/static/assets/flowDiagram-A5DVABFB-BppFxEKw.js +1 -0
- ksadk/server/static/assets/{ganttDiagram-EL5Y4UJY-TWIxoyYL.js → ganttDiagram-EL5Y4UJY-DDaG1f4_.js} +1 -1
- ksadk/server/static/assets/gitGraph-4MIJSDKK-B-AhYj4C.js +1 -0
- ksadk/server/static/assets/{gitGraphDiagram-WWUBYQGX-DDh2hd2S.js → gitGraphDiagram-WWUBYQGX-DcxzRwYC.js} +1 -1
- ksadk/server/static/assets/index-BMd2mucF.css +2 -0
- ksadk/server/static/assets/index-D5qyyhTH.js +244 -0
- ksadk/server/static/assets/info-A6RAGUB7-0uiKrC35.js +1 -0
- ksadk/server/static/assets/{infoDiagram-RXCK75RN-CgoyWiDR.js → infoDiagram-RXCK75RN-CYMdn6lQ.js} +1 -1
- ksadk/server/static/assets/{ishikawaDiagram-5VMMS53U-CyRIu_om.js → ishikawaDiagram-5VMMS53U-CLHFAD6c.js} +1 -1
- ksadk/server/static/assets/{journeyDiagram-EYS64GPL-DN5JXHzN.js → journeyDiagram-EYS64GPL-BHSKQGAl.js} +1 -1
- ksadk/server/static/assets/{kanban-definition-3QL26DDD-CNP3ui66.js → kanban-definition-3QL26DDD-D5W3tjTT.js} +1 -1
- ksadk/server/static/assets/{katex-DWEzQy1d.js → katex-mtjOi4KM.js} +1 -1
- ksadk/server/static/assets/{linear-C-qHp7d2.js → linear-BETqsxE_.js} +1 -1
- ksadk/server/static/assets/{mermaid-parser.core-Cl-K943T.js → mermaid-parser.core-DPWrvs90.js} +2 -2
- ksadk/server/static/assets/{mindmap-definition-FBJOCRG2-D5DB5mYm.js → mindmap-definition-FBJOCRG2-CJG2mfBP.js} +1 -1
- ksadk/server/static/assets/packet-AYTQ26CC-BVTmZ_cw.js +1 -0
- ksadk/server/static/assets/{pegDiagram-XKGWAZYB-BQVc9ia_.js → pegDiagram-XKGWAZYB-GjAPNaqn.js} +1 -1
- ksadk/server/static/assets/pie-WAS4IAKB-DyfxBU7V.js +1 -0
- ksadk/server/static/assets/{pieDiagram-E7YTZNPT-B6UCAvtY.js → pieDiagram-E7YTZNPT-ToIewrqr.js} +1 -1
- ksadk/server/static/assets/{quadrantDiagram-AXDQQJYC-mIr2d3eb.js → quadrantDiagram-AXDQQJYC-4d78KiEY.js} +1 -1
- ksadk/server/static/assets/radar-RG4KPBEZ-FFontjL2.js +1 -0
- ksadk/server/static/assets/railroad-74A4TZTK-D0Uidzen.js +1 -0
- ksadk/server/static/assets/railroad-abnf-HS5TGJTU-BOo_9T2h.js +1 -0
- ksadk/server/static/assets/railroad-ebnf-LZEXJU2U-D6-BjW7Z.js +1 -0
- ksadk/server/static/assets/railroad-peg-WCYAUIDC-CXx__yMm.js +1 -0
- ksadk/server/static/assets/{railroadDiagram-O6MQD6OU-Dt3FFDmK.js → railroadDiagram-O6MQD6OU-DKzx4xfv.js} +1 -1
- ksadk/server/static/assets/{requirementDiagram-EFPCY7ZU-Cz_vQtgF.js → requirementDiagram-EFPCY7ZU-CJYljxQS.js} +1 -1
- ksadk/server/static/assets/{sankeyDiagram-P5KCCOFB-wlO1TsH5.js → sankeyDiagram-P5KCCOFB-DLU2fV1w.js} +1 -1
- ksadk/server/static/assets/{sequenceDiagram-WJ2MYXX4-CHRbElYq.js → sequenceDiagram-WJ2MYXX4-CVqysrA9.js} +1 -1
- ksadk/server/static/assets/{sizeCapture-X5ZJPWSS-DXX54iyO.js → sizeCapture-X5ZJPWSS-BUxfmTHc.js} +1 -1
- ksadk/server/static/assets/{stateDiagram-HBIQ2CUA-Dp_lw9II.js → stateDiagram-HBIQ2CUA-CNFgYI9E.js} +1 -1
- ksadk/server/static/assets/stateDiagram-v2-4QOOHH4V-BXAFScz8.js +1 -0
- ksadk/server/static/assets/{swimlanes-XN3QIQJK-D8O5UlLG.js → swimlanes-XN3QIQJK-B6qCMe8N.js} +1 -1
- ksadk/server/static/assets/swimlanesDiagram-VK2B7HYN-uIQ2lJKH.js +8 -0
- ksadk/server/static/assets/{timeline-definition-24CTP7MA-8mfKcxvG.js → timeline-definition-24CTP7MA-CMD3x_U_.js} +1 -1
- ksadk/server/static/assets/treeView-Q6P3EWNA-CK5GgucZ.js +1 -0
- ksadk/server/static/assets/treemap-WGGIJYW6-Bq1u1SpG.js +1 -0
- ksadk/server/static/assets/{vennDiagram-4TSXK5OY-EnNl0DE7.js → vennDiagram-4TSXK5OY-BqtUqvXM.js} +1 -1
- ksadk/server/static/assets/wardley-WFR3VGLG-QAt06uEz.js +1 -0
- ksadk/server/static/assets/{wardleyDiagram-VM6X3IG4-DBu5fbYr.js → wardleyDiagram-VM6X3IG4-B5KfDjI-.js} +1 -1
- ksadk/server/static/assets/{xychartDiagram-S5SC5T6Z-BSVXs4IY.js → xychartDiagram-S5SC5T6Z-BaUlSZrn.js} +1 -1
- ksadk/server/static/index.html +2 -2
- ksadk/server/terminal_sessions.py +34 -2
- ksadk/session_context.py +66 -0
- ksadk/sessions/__init__.py +11 -21
- ksadk/sessions/_local_service_sync.py +263 -1
- ksadk/sessions/base.py +176 -0
- ksadk/sessions/errors.py +4 -0
- ksadk/sessions/in_memory.py +309 -19
- ksadk/sessions/invocation_identity.py +235 -0
- ksadk/sessions/local_service.py +83 -1
- ksadk/sessions/persistence.py +275 -0
- ksadk/sessions/postgres_service.py +358 -0
- ksadk/sessions/resilience.py +8 -0
- ksadk/sessions/resilient.py +362 -4
- ksadk/sessions/topology.py +135 -0
- ksadk/skills/__init__.py +26 -0
- ksadk/skills/events.py +464 -0
- ksadk/skills/manifest_cache.py +263 -0
- ksadk/skills/mcp_server/__init__.py +0 -0
- ksadk/skills/mcp_server/_registry.py +33 -0
- ksadk/skills/mcp_server/_routing.py +26 -0
- ksadk/skills/mcp_server/_workspace_files.py +160 -0
- ksadk/skills/mcp_server/register.py +415 -0
- ksadk/skills/mcp_server/server.py +218 -0
- ksadk/skills/models.py +37 -4
- ksadk/skills/observability.py +132 -0
- ksadk/skills/package_store.py +261 -40
- ksadk/skills/runtime/agent.py +67 -9
- ksadk/skills/runtime/artifact_delivery.py +139 -0
- ksadk/skills/runtime/artifacts.py +87 -0
- ksadk/skills/runtime/backends/e2b.py +460 -27
- ksadk/skills/runtime/backends/local.py +629 -62
- ksadk/skills/runtime/base.py +81 -7
- ksadk/skills/runtime/executor.py +268 -1
- ksadk/skills/runtime/loader.py +118 -5
- ksadk/skills/runtime/pinned.py +114 -0
- ksadk/skills/runtime/request.py +49 -0
- ksadk/skills/service_client.py +167 -28
- ksadk/skills/tool_defs.py +108 -3
- ksadk/studio/api.py +810 -93
- ksadk/studio/api_catalog_routes.py +18 -0
- ksadk/studio/api_contracts.py +8 -2
- ksadk/studio/api_plugin_routes.py +562 -104
- ksadk/studio/api_resource_connections.py +221 -0
- ksadk/studio/authoring.py +12 -6
- ksadk/studio/authoring_coordinator.py +23 -16
- ksadk/studio/builder.py +88 -0
- ksadk/studio/capabilities.py +23 -6
- ksadk/studio/channel_connections.py +333 -0
- ksadk/studio/cloud.py +370 -112
- ksadk/studio/cloud_shared_web.py +638 -0
- ksadk/studio/codex_agent_service.py +130 -28
- ksadk/studio/codex_builder.py +242 -3
- ksadk/studio/codex_credentials.py +90 -0
- ksadk/studio/codex_manifest.py +31 -4
- ksadk/studio/codex_plugin_store.py +746 -0
- ksadk/studio/codex_provider_build.py +231 -0
- ksadk/studio/codex_run.py +190 -26
- ksadk/studio/compatibility_report.py +12 -1
- ksadk/studio/compiler.py +88 -14
- ksadk/studio/configuration.py +182 -0
- ksadk/studio/contracts.py +165 -11
- ksadk/studio/dsh_application.py +247 -0
- ksadk/studio/dsh_capability_service.py +1374 -0
- ksadk/studio/dsh_lifecycle.py +58 -0
- ksadk/studio/dsh_models.py +59 -0
- ksadk/studio/dsh_provider_registration.py +367 -21
- ksadk/studio/entry.py +53 -0
- ksadk/studio/event_store.py +36 -5
- ksadk/studio/execution_host.py +525 -0
- ksadk/studio/framework_run.py +32 -10
- ksadk/studio/hosted_kernel.py +89 -14
- ksadk/studio/kernel_registry.py +418 -0
- ksadk/studio/kernel_sqlite_migration.py +181 -0
- ksadk/studio/mcp_runtime.py +11 -3
- ksadk/studio/model_client.py +1180 -66
- ksadk/studio/model_profile_service.py +26 -11
- ksadk/studio/native_documents.py +172 -0
- ksadk/studio/plugin_composition.py +202 -24
- ksadk/studio/plugin_kernel_adapter.py +21 -106
- ksadk/studio/plugin_lifecycle.py +80 -0
- ksadk/studio/plugin_runtime.py +448 -56
- ksadk/studio/provider_recovery.py +59 -0
- ksadk/studio/recovery_page.py +70 -0
- ksadk/studio/resource_authority.py +664 -0
- ksadk/studio/resource_binding_validation.py +77 -0
- ksadk/studio/resource_build_admission.py +196 -0
- ksadk/studio/resource_build_materializer.py +149 -0
- ksadk/studio/resource_catalog.py +149 -2
- ksadk/studio/resource_connections.py +220 -0
- ksadk/studio/run_activity.py +895 -0
- ksadk/studio/run_documents.py +127 -0
- ksadk/studio/run_service.py +789 -83
- ksadk/studio/runtime_catalog.py +53 -10
- ksadk/studio/runtime_source.py +16 -8
- ksadk/studio/scheduler_assistant.py +317 -0
- ksadk/studio/scheduler_runtime.py +32 -280
- ksadk/studio/scheduler_service.py +3 -0
- ksadk/studio/service.py +861 -120
- ksadk/studio/shared_web.py +579 -82
- ksadk/studio/static/assets/ChannelsPage-D_PGmKVq.js +8 -0
- ksadk/studio/static/assets/CodeBlock-DMIf4h33-UIqo-BPW.js +44 -0
- ksadk/studio/static/assets/ExecutionTree-v3HexvPo-Bk8wzGSy.js +1 -0
- ksadk/studio/static/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
- ksadk/studio/static/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
- ksadk/studio/static/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
- ksadk/studio/static/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
- ksadk/studio/static/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
- ksadk/studio/static/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
- ksadk/studio/static/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
- ksadk/studio/static/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
- ksadk/studio/static/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
- ksadk/studio/static/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
- ksadk/studio/static/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
- ksadk/studio/static/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
- ksadk/studio/static/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
- ksadk/studio/static/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
- ksadk/studio/static/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
- ksadk/studio/static/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
- ksadk/studio/static/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
- ksadk/studio/static/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
- ksadk/studio/static/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
- ksadk/studio/static/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
- ksadk/studio/static/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
- ksadk/studio/static/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
- ksadk/studio/static/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
- ksadk/studio/static/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
- ksadk/studio/static/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
- ksadk/studio/static/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
- ksadk/studio/static/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
- ksadk/studio/static/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
- ksadk/studio/static/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
- ksadk/studio/static/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
- ksadk/studio/static/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
- ksadk/studio/static/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
- ksadk/studio/static/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
- ksadk/studio/static/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
- ksadk/studio/static/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
- ksadk/studio/static/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
- ksadk/studio/static/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
- ksadk/studio/static/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
- ksadk/studio/static/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
- ksadk/studio/static/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
- ksadk/studio/static/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
- ksadk/studio/static/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
- ksadk/studio/static/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
- ksadk/studio/static/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
- ksadk/studio/static/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
- ksadk/studio/static/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
- ksadk/studio/static/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
- ksadk/studio/static/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
- ksadk/studio/static/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
- ksadk/studio/static/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
- ksadk/studio/static/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
- ksadk/studio/static/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
- ksadk/studio/static/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
- ksadk/studio/static/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
- ksadk/studio/static/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
- ksadk/studio/static/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
- ksadk/studio/static/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
- ksadk/studio/static/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
- ksadk/studio/static/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
- ksadk/studio/static/assets/MathMessageMarkdown-BesSeCYl-BEOyPC3w.css +1 -0
- ksadk/studio/static/assets/MathMessageMarkdown-BesSeCYl-OhNwH4K9.js +8 -0
- ksadk/studio/static/assets/MermaidBlock-H_0qzQDf-BdXtxug_.js +11 -0
- ksadk/studio/static/assets/MessageMarkdown-CwMTvgFj-g0I-I4dD.js +59 -0
- ksadk/studio/static/assets/PrismRenderer-5pK6tUdb.js +3 -0
- ksadk/studio/static/assets/TeamsPage-DqX4HWKQ.js +3 -0
- ksadk/studio/static/assets/abnfDiagram-VCTEODGH-Bnb1O3tA.js +1 -0
- ksadk/studio/static/assets/arc-Pry-7G1_.js +1 -0
- ksadk/studio/static/assets/architecture-7GRP2DOG-BMOPAkUC.js +1 -0
- ksadk/studio/static/assets/architectureDiagram-5GKGNRK7-5Zn1gqJv.js +36 -0
- ksadk/studio/static/assets/array-BifhSqXX.js +1 -0
- ksadk/studio/static/assets/blockDiagram-I7D4REHJ-CLB4L11u.js +129 -0
- ksadk/studio/static/assets/c4Diagram-7LVT6UL2-BOma_Z_F.js +38 -0
- ksadk/studio/static/assets/channel-r8itPEik.js +1 -0
- ksadk/studio/static/assets/chunk-2E4U76K2-BFf_huql.js +2 -0
- ksadk/studio/static/assets/chunk-2Q5K7J3B-C1jixKkw.js +1 -0
- ksadk/studio/static/assets/chunk-2ZTRR5NV-C2qZJila.js +2 -0
- ksadk/studio/static/assets/chunk-3Z5EZCMW-DMniOlPd.js +1 -0
- ksadk/studio/static/assets/chunk-4HAMMTFA-DVtDP4rm.js +62 -0
- ksadk/studio/static/assets/chunk-5V3GS4D5-i-ZDOEnP.js +1 -0
- ksadk/studio/static/assets/chunk-5VM5RSS4-ZNzvKenW.js +15 -0
- ksadk/studio/static/assets/chunk-6AZGARVD-BB6FqMl-.js +1 -0
- ksadk/studio/static/assets/chunk-6EIED4P4-DdUJkP0f.js +1 -0
- ksadk/studio/static/assets/chunk-6K3QC6MW-CqVsVn-y.js +1 -0
- ksadk/studio/static/assets/chunk-6TQVIW2G-DKlLbNIP.js +1 -0
- ksadk/studio/static/assets/chunk-747NJXEK-BFkZPlab.js +2 -0
- ksadk/studio/static/assets/chunk-75Z2AOVW-BZNhM4vB.js +2 -0
- ksadk/studio/static/assets/chunk-CLGD4ZFX-D-GTtyKs.js +1 -0
- ksadk/studio/static/assets/chunk-DU6HZSFF-CpEZCUgg.js +128 -0
- ksadk/studio/static/assets/chunk-F27PBJKO-D7pvKf0C.js +1 -0
- ksadk/studio/static/assets/chunk-FOHPRMQF-DHwB1DNv.js +161 -0
- ksadk/studio/static/assets/chunk-GMAD6QVW-DE_8H717.js +72 -0
- ksadk/studio/static/assets/chunk-GVQU2GXP-BfEB0lNv.js +1 -0
- ksadk/studio/static/assets/chunk-I5DQTOEV-Hm2LfiYI.js +1 -0
- ksadk/studio/static/assets/chunk-ICYGCRZG-749o-pGJ.js +1 -0
- ksadk/studio/static/assets/chunk-IH6LHLGP-CC6I9F3i.js +1 -0
- ksadk/studio/static/assets/chunk-IMKFNOWR-D38axYOR.js +231 -0
- ksadk/studio/static/assets/chunk-JWPE2WC7-DVXcaiue.js +1 -0
- ksadk/studio/static/assets/chunk-KI3K4JFJ-Rma-0k_M.js +1 -0
- ksadk/studio/static/assets/chunk-L3NEJ4N5-Dq5xn7aL.js +1 -0
- ksadk/studio/static/assets/chunk-LNGE3PJU-DWpGwHrW.js +34 -0
- ksadk/studio/static/assets/chunk-OSK3NFVY-DiCQjx9b.js +10 -0
- ksadk/studio/static/assets/chunk-OUJLGHUK-D0j2JRSg.js +2 -0
- ksadk/studio/static/assets/chunk-P2QGCYS3-VdhKmfYq.js +1 -0
- ksadk/studio/static/assets/chunk-POPQ4Y6H-C1_rzO5M.js +1 -0
- ksadk/studio/static/assets/chunk-PWAF6VOD-B45HvflI.js +1 -0
- ksadk/studio/static/assets/chunk-SHT3W25Y-DsCFnRVE.js +168 -0
- ksadk/studio/static/assets/chunk-SVP7TREG-DljApgRX.js +88 -0
- ksadk/studio/static/assets/chunk-TICWLB2K-DWqRtyqa.js +206 -0
- ksadk/studio/static/assets/chunk-TLUHSLCS-DUeGgnD5.js +2 -0
- ksadk/studio/static/assets/chunk-UY3FDG6J-CnmaAkG5.js +1 -0
- ksadk/studio/static/assets/chunk-XHIXRSVI-BFQ6B8BE.js +1 -0
- ksadk/studio/static/assets/chunk-XXDRQBXY-DqAYeljN.js +1 -0
- ksadk/studio/static/assets/chunk-Y2CYZVJY-DsF7k-Jl.js +1 -0
- ksadk/studio/static/assets/classDiagram-ZZMXUADV-DjeMNJmu.js +1 -0
- ksadk/studio/static/assets/classDiagram-v2-VYDZK3BY-DjeMNJmu.js +1 -0
- ksadk/studio/static/assets/clsx-DB0hHKMi.js +1 -0
- ksadk/studio/static/assets/cose-bilkent-JH36ORCC-om8AY0Mc.js +1 -0
- ksadk/studio/static/assets/createLucideIcon-DR0LGTP5.js +1 -0
- ksadk/studio/static/assets/cynefin-OW5HDTMX-7tP7QfM3.js +1 -0
- ksadk/studio/static/assets/cynefinDiagram-5FMLGOSQ-6BrmK52S.js +62 -0
- ksadk/studio/static/assets/cytoscape.esm-CGd-uY3x.js +321 -0
- ksadk/studio/static/assets/dagre-GXQ25YYZ-buZBRlwz.js +4 -0
- ksadk/studio/static/assets/dagre-PrKaheQc.js +1 -0
- ksadk/studio/static/assets/defaultLocale-BFoDCU3G.js +1 -0
- ksadk/studio/static/assets/diagram-S7CK7UJ4-C6aH78c4.js +30 -0
- ksadk/studio/static/assets/diagram-UQ7AKVKN-DY_tQ6C_.js +41 -0
- ksadk/studio/static/assets/diagram-VSXAHHWV-DX9p5cQT.js +3 -0
- ksadk/studio/static/assets/diagram-VX7I27RA-DB2WSH8X.js +24 -0
- ksadk/studio/static/assets/diagram-Z3DM3KII-Ce1BJwHE.js +24 -0
- ksadk/studio/static/assets/dist-v5Q1xZ2K.js +1 -0
- ksadk/studio/static/assets/ebnfDiagram-PWID7BFC-Daukxwqf.js +1 -0
- ksadk/studio/static/assets/erDiagram-RLTQ6QDP-CGthTMmJ.js +99 -0
- ksadk/studio/static/assets/eventmodeling-NTZA5JFV-BR2FzK4R.js +1 -0
- ksadk/studio/static/assets/flowDiagram-HODETNUW-CZ1tEenp.js +1 -0
- ksadk/studio/static/assets/ganttDiagram-EL5Y4UJY-BfoAZW9X.js +292 -0
- ksadk/studio/static/assets/gitGraph-4MIJSDKK-DYlXv_yJ.js +1 -0
- ksadk/studio/static/assets/gitGraphDiagram-WWUBYQGX-S985OIJF.js +106 -0
- ksadk/studio/static/assets/graphlib-DS17s2tU.js +1 -0
- ksadk/studio/static/assets/iconfont-4HN3C9Pg.woff2 +0 -0
- ksadk/studio/static/assets/index-CfEnms6Y.css +2 -0
- ksadk/studio/static/assets/index-Dp61rOWZ.js +0 -0
- ksadk/studio/static/assets/index.dom-BQenBtFm.js +1 -0
- ksadk/studio/static/assets/info-A6RAGUB7-BlU-8_xu.js +1 -0
- ksadk/studio/static/assets/infoDiagram-27XIBGKW-CO3W8tFn.js +2 -0
- ksadk/studio/static/assets/init-C-OQMol4.js +1 -0
- ksadk/studio/static/assets/ishikawaDiagram-5VMMS53U-BUGEl8cO.js +70 -0
- ksadk/studio/static/assets/journeyDiagram-3NMN7TZE-Ch2ZYxvr.js +139 -0
- ksadk/studio/static/assets/kanban-definition-UXKFOSKX-Da-Fp4vC.js +89 -0
- ksadk/studio/static/assets/katex-DhON-x74.js +257 -0
- ksadk/studio/static/assets/kingsoft-cloud-BOcLiQ3e.ico +0 -0
- ksadk/studio/static/assets/line-BDaeq300.js +1 -0
- ksadk/studio/static/assets/linear-DmucvK78.js +1 -0
- ksadk/studio/static/assets/mermaid-parser.core-Cj5mYdBi.js +4 -0
- ksadk/studio/static/assets/mindmap-definition-YA3MSWOX-CvlsVQ38.js +96 -0
- ksadk/studio/static/assets/ordinal-BDEzSJ7C.js +1 -0
- ksadk/studio/static/assets/packet-AYTQ26CC-BSJEH9Lb.js +1 -0
- ksadk/studio/static/assets/path-fybaL0A-.js +1 -0
- ksadk/studio/static/assets/pegDiagram-XKGWAZYB-BuHuNgT5.js +1 -0
- ksadk/studio/static/assets/pie-WAS4IAKB-qxv01Y8W.js +1 -0
- ksadk/studio/static/assets/pieDiagram-E7YTZNPT-DlrWiSOv.js +39 -0
- ksadk/studio/static/assets/preload-helper-CO68xz6u.js +1 -0
- ksadk/studio/static/assets/presentation-yYw7xK60-BmcZriri.js +1 -0
- ksadk/studio/static/assets/quadrantDiagram-AXDQQJYC-BA3hpUZM.js +7 -0
- ksadk/studio/static/assets/radar-RG4KPBEZ-B6h6pWOs.js +1 -0
- ksadk/studio/static/assets/railroad-74A4TZTK-CGtTWdGc.js +1 -0
- ksadk/studio/static/assets/railroad-abnf-HS5TGJTU-Cq7y22IA.js +1 -0
- ksadk/studio/static/assets/railroad-ebnf-LZEXJU2U-V0aQVv9m.js +1 -0
- ksadk/studio/static/assets/railroad-peg-WCYAUIDC-BQiIsX_p.js +1 -0
- ksadk/studio/static/assets/railroadDiagram-O6MQD6OU-BxpAazym.js +1 -0
- ksadk/studio/static/assets/requirementDiagram-BXWQKSXE-E-Ge0FcV.js +84 -0
- ksadk/studio/static/assets/rolldown-runtime-Dy4uBu1J-DK3Fl9T5.js +1 -0
- ksadk/studio/static/assets/rough.esm-Dy-Kn_BL.js +1 -0
- ksadk/studio/static/assets/sankeyDiagram-P5KCCOFB-CA6YKhQ3.js +40 -0
- ksadk/studio/static/assets/sequenceDiagram-WJ2MYXX4-CcElXGg4.js +162 -0
- ksadk/studio/static/assets/sizeCapture-INFHLROL-B0uUizjq.js +1 -0
- ksadk/studio/static/assets/src-B6xuSHsQ.js +1 -0
- ksadk/studio/static/assets/stateDiagram-D77RDMKH-DdXZOFqY.js +1 -0
- ksadk/studio/static/assets/stateDiagram-v2-MP3YSRHH-CJbOM-QK.js +1 -0
- ksadk/studio/static/assets/swimlanes-42K2YHIH-NtgEEHLY.js +1 -0
- ksadk/studio/static/assets/swimlanesDiagram-VR7AAH4N-D-AVlW8B.js +8 -0
- ksadk/studio/static/assets/timeline-definition-24CTP7MA-CIuYhNy3.js +120 -0
- ksadk/studio/static/assets/treeView-Q6P3EWNA-BTTYcvrF.js +1 -0
- ksadk/studio/static/assets/treemap-WGGIJYW6-Cg7tyfH8.js +1 -0
- ksadk/studio/static/assets/vennDiagram-4TSXK5OY-7HbApbrv.js +34 -0
- ksadk/studio/static/assets/wardley-WFR3VGLG-C6N1rULY.js +1 -0
- ksadk/studio/static/assets/wardleyDiagram-VM6X3IG4-CMJiaedy.js +78 -0
- ksadk/studio/static/assets/xychartDiagram-S5SC5T6Z-BdgtMvW2.js +7 -0
- ksadk/studio/static/index.html +8 -2
- ksadk/studio/teams_catalog.py +133 -0
- ksadk/studio/teams_installation.py +214 -0
- ksadk/studio/teams_node.py +545 -0
- ksadk/studio/teams_node_runner.py +69 -0
- ksadk/studio/teams_remote.py +274 -0
- ksadk/studio/templates.py +40 -19
- ksadk/studio/validator.py +13 -0
- ksadk/studio/workspace.py +19 -4
- ksadk/studio/workspace_plugins.py +136 -0
- ksadk/studio/workspace_registry.py +286 -0
- ksadk/toolsets/__init__.py +11 -0
- ksadk/toolsets/skills.py +446 -378
- ksadk/toolsets/workspace.py +3 -3
- ksadk/toolsets/workspace_identity.py +15 -0
- ksadk/version.py +1 -1
- ksadk_runtime_common/memory_backend/providers/lancedb.py +56 -2
- ksadk_runtime_common/schemas/memory_backend_manifest.schema.json +148 -148
- ksadk_runtime_common/workspace_files/router.py +26 -13
- agentengine_sdk_python-0.8.3.dist-info/RECORD +0 -833
- ksadk/server/static/assets/MathMessageMarkdown-D9uB55Oh.js +0 -8
- ksadk/server/static/assets/architecture-7GRP2DOG-Dk9nRQ6-.js +0 -1
- ksadk/server/static/assets/channel-s354Yo6o.js +0 -1
- ksadk/server/static/assets/chunk-2Q5K7J3B-CDpPpKR5.js +0 -1
- ksadk/server/static/assets/classDiagram-DTDB5LWJ-Dd0nucl9.js +0 -1
- ksadk/server/static/assets/classDiagram-v2-JRS7N3AN-Dd0nucl9.js +0 -1
- ksadk/server/static/assets/cynefin-OW5HDTMX-BBlT4uMf.js +0 -1
- ksadk/server/static/assets/dist-DdRBIceA.js +0 -1
- ksadk/server/static/assets/eventmodeling-NTZA5JFV-Dr5okd8V.js +0 -1
- ksadk/server/static/assets/flowDiagram-A5DVABFB-VEO-cVcO.js +0 -1
- ksadk/server/static/assets/gitGraph-4MIJSDKK-CKiKY9Cf.js +0 -1
- ksadk/server/static/assets/index-B2k_urY8.js +0 -241
- ksadk/server/static/assets/index-ILPt6IWF.css +0 -2
- ksadk/server/static/assets/info-A6RAGUB7-ubA4Nm_p.js +0 -1
- ksadk/server/static/assets/packet-AYTQ26CC-C7R3NUlK.js +0 -1
- ksadk/server/static/assets/pie-WAS4IAKB-BIT6qjr3.js +0 -1
- ksadk/server/static/assets/radar-RG4KPBEZ-l5noef9X.js +0 -1
- ksadk/server/static/assets/railroad-74A4TZTK-Baea6ZvM.js +0 -1
- ksadk/server/static/assets/railroad-abnf-HS5TGJTU-OKn8zZnU.js +0 -1
- ksadk/server/static/assets/railroad-ebnf-LZEXJU2U-DdKAKC4l.js +0 -1
- ksadk/server/static/assets/railroad-peg-WCYAUIDC-Bds8udlG.js +0 -1
- ksadk/server/static/assets/stateDiagram-v2-4QOOHH4V-5b-8o2iS.js +0 -1
- ksadk/server/static/assets/swimlanesDiagram-VK2B7HYN-uf6WPRA4.js +0 -8
- ksadk/server/static/assets/treeView-Q6P3EWNA-CK5CSrSO.js +0 -1
- ksadk/server/static/assets/treemap-WGGIJYW6-Ch2BKgAM.js +0 -1
- ksadk/server/static/assets/wardley-WFR3VGLG-YW3Q0ml_.js +0 -1
- ksadk/studio/static/assets/PrismRenderer-IYN-ffww.js +0 -3
- ksadk/studio/static/assets/index-DXs1Eq4M.css +0 -1
- ksadk/studio/static/assets/index-DeDqwTsD.js +0 -283
- {agentengine_sdk_python-0.8.3.dist-info → agentengine_sdk_python-0.8.5.dist-info}/WHEEL +0 -0
- {agentengine_sdk_python-0.8.3.dist-info → agentengine_sdk_python-0.8.5.dist-info}/entry_points.txt +0 -0
- {agentengine_sdk_python-0.8.3.dist-info → agentengine_sdk_python-0.8.5.dist-info}/licenses/LICENSE +0 -0
- {agentengine_sdk_python-0.8.3.dist-info → agentengine_sdk_python-0.8.5.dist-info}/top_level.txt +0 -0
ksadk/studio/run_service.py
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
import asyncio
|
|
6
|
+
import copy
|
|
6
7
|
import hashlib
|
|
7
8
|
import json
|
|
8
9
|
import logging
|
|
@@ -14,6 +15,8 @@ from pathlib import Path
|
|
|
14
15
|
from typing import Any
|
|
15
16
|
from uuid import uuid4
|
|
16
17
|
|
|
18
|
+
from jsonschema import Draft202012Validator
|
|
19
|
+
|
|
17
20
|
from ksadk.agui.a2ui_projection import project_a2ui_operations
|
|
18
21
|
from ksadk.conversations.projector import (
|
|
19
22
|
project_conversation_item,
|
|
@@ -60,14 +63,17 @@ from ksadk.runtime import (
|
|
|
60
63
|
RuntimeLaunchContext,
|
|
61
64
|
StartRequest,
|
|
62
65
|
)
|
|
66
|
+
from ksadk.runtime.executor import RuntimeStartPreparation
|
|
63
67
|
from ksadk.sessions.base import BaseSessionService
|
|
64
68
|
from ksadk.sessions.local_service import LocalSessionService
|
|
65
69
|
from ksadk.studio.contracts import RunEvent, RunRecord, RunStatus, Usage
|
|
66
70
|
from ksadk.studio.errors import StudioError
|
|
67
71
|
from ksadk.studio.event_store import RunEventStore
|
|
72
|
+
from ksadk.studio.run_activity import project_run_activities
|
|
68
73
|
from ksadk.studio.workspace import Workspace
|
|
69
74
|
|
|
70
75
|
_CANCEL_TIMEOUT_SECONDS = 2.0
|
|
76
|
+
_INTERACTION_SUBMIT_TIMEOUT_SECONDS = 10.0
|
|
71
77
|
|
|
72
78
|
|
|
73
79
|
logger = logging.getLogger(__name__)
|
|
@@ -86,6 +92,129 @@ class StudioRunSpec:
|
|
|
86
92
|
plugin_bundle_root: Path | None = None
|
|
87
93
|
|
|
88
94
|
|
|
95
|
+
class _ProjectedRunEventWriter:
|
|
96
|
+
"""Batch high-frequency text deltas before rewriting a Run JSON file.
|
|
97
|
+
|
|
98
|
+
Canonical RuntimeEvents are already durable in the session store. The
|
|
99
|
+
RunEvent JSON is a presentation projection, so writing and retaining one
|
|
100
|
+
copy of the full projection envelope per token is unnecessary and becomes
|
|
101
|
+
quadratic as the response grows. A short batching window keeps live chat
|
|
102
|
+
responsive and coalesces adjacent chunks for the same projected item.
|
|
103
|
+
"""
|
|
104
|
+
|
|
105
|
+
_STREAM_TYPES = frozenset({"message.delta", "thinking.delta"})
|
|
106
|
+
|
|
107
|
+
def __init__(
|
|
108
|
+
self,
|
|
109
|
+
event_store: RunEventStore,
|
|
110
|
+
run_id: str,
|
|
111
|
+
on_event: Callable[[RunEvent], None] | None,
|
|
112
|
+
*,
|
|
113
|
+
flush_interval: float = 0.5,
|
|
114
|
+
) -> None:
|
|
115
|
+
self._event_store = event_store
|
|
116
|
+
self._run_id = run_id
|
|
117
|
+
self._on_event = on_event
|
|
118
|
+
self._flush_interval = flush_interval
|
|
119
|
+
self._pending: list[tuple[str, dict[str, Any]]] = []
|
|
120
|
+
# Publish the first chunk immediately so the UI can enter streaming
|
|
121
|
+
# state without waiting for the batching window. Subsequent chunks are
|
|
122
|
+
# coalesced into short durable writes.
|
|
123
|
+
self._last_flush = 0.0
|
|
124
|
+
self._flush_handle: asyncio.TimerHandle | None = None
|
|
125
|
+
|
|
126
|
+
def append(self, event_type: str, data: dict[str, Any]) -> RunEvent | None:
|
|
127
|
+
if event_type in self._STREAM_TYPES:
|
|
128
|
+
self._pending.append((event_type, data))
|
|
129
|
+
elapsed = time.monotonic() - self._last_flush
|
|
130
|
+
if elapsed < self._flush_interval:
|
|
131
|
+
if self._flush_handle is None:
|
|
132
|
+
self._flush_handle = asyncio.get_running_loop().call_later(
|
|
133
|
+
self._flush_interval - elapsed, self.flush
|
|
134
|
+
)
|
|
135
|
+
return None
|
|
136
|
+
return self.flush()[-1]
|
|
137
|
+
self.flush()
|
|
138
|
+
stored = self._event_store.append(self._run_id, event_type, data)
|
|
139
|
+
self._notify((stored,))
|
|
140
|
+
return stored
|
|
141
|
+
|
|
142
|
+
def flush(self) -> list[RunEvent]:
|
|
143
|
+
if self._flush_handle is not None:
|
|
144
|
+
self._flush_handle.cancel()
|
|
145
|
+
self._flush_handle = None
|
|
146
|
+
if not self._pending:
|
|
147
|
+
return []
|
|
148
|
+
pending, self._pending = self._pending, []
|
|
149
|
+
stored = self._event_store.append_many(
|
|
150
|
+
self._run_id,
|
|
151
|
+
self._coalesce_stream_entries(pending),
|
|
152
|
+
)
|
|
153
|
+
self._last_flush = time.monotonic()
|
|
154
|
+
self._notify(stored)
|
|
155
|
+
return stored
|
|
156
|
+
|
|
157
|
+
@staticmethod
|
|
158
|
+
def _coalesce_stream_entries(
|
|
159
|
+
entries: list[tuple[str, dict[str, Any]]],
|
|
160
|
+
) -> list[tuple[str, dict[str, Any]]]:
|
|
161
|
+
"""Collapse adjacent chunks without changing the rendered transcript."""
|
|
162
|
+
|
|
163
|
+
coalesced: list[tuple[str, dict[str, Any]]] = []
|
|
164
|
+
for event_type, data in entries:
|
|
165
|
+
if not coalesced:
|
|
166
|
+
coalesced.append((event_type, copy.deepcopy(data)))
|
|
167
|
+
continue
|
|
168
|
+
previous_type, previous = coalesced[-1]
|
|
169
|
+
if not _same_projected_stream(previous_type, previous, event_type, data):
|
|
170
|
+
coalesced.append((event_type, copy.deepcopy(data)))
|
|
171
|
+
continue
|
|
172
|
+
|
|
173
|
+
operation = str(data.get("operation") or "append")
|
|
174
|
+
if operation == "append":
|
|
175
|
+
merged_text = str(previous.get("text") or "") + str(data.get("text") or "")
|
|
176
|
+
merged = copy.deepcopy(data)
|
|
177
|
+
_set_projected_stream_text(merged, merged_text)
|
|
178
|
+
coalesced[-1] = (event_type, merged)
|
|
179
|
+
else:
|
|
180
|
+
coalesced[-1] = (event_type, copy.deepcopy(data))
|
|
181
|
+
return coalesced
|
|
182
|
+
|
|
183
|
+
def _notify(self, events: tuple[RunEvent, ...] | list[RunEvent]) -> None:
|
|
184
|
+
if self._on_event is None:
|
|
185
|
+
return
|
|
186
|
+
for event in events:
|
|
187
|
+
self._on_event(event)
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def _same_projected_stream(
|
|
191
|
+
previous_type: str,
|
|
192
|
+
previous: dict[str, Any],
|
|
193
|
+
event_type: str,
|
|
194
|
+
data: dict[str, Any],
|
|
195
|
+
) -> bool:
|
|
196
|
+
return (
|
|
197
|
+
previous_type == event_type
|
|
198
|
+
and previous.get("itemId") == data.get("itemId")
|
|
199
|
+
and previous.get("partId") == data.get("partId")
|
|
200
|
+
and previous.get("operation") == data.get("operation")
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
def _set_projected_stream_text(data: dict[str, Any], text: str) -> None:
|
|
205
|
+
data["text"] = text
|
|
206
|
+
conversation_item = data.get("conversationItem")
|
|
207
|
+
if isinstance(conversation_item, dict):
|
|
208
|
+
payload = conversation_item.get("payload")
|
|
209
|
+
if isinstance(payload, dict):
|
|
210
|
+
payload["text"] = text
|
|
211
|
+
runtime_event = data.get("runtimeEvent")
|
|
212
|
+
if isinstance(runtime_event, dict):
|
|
213
|
+
update = runtime_event.get("update")
|
|
214
|
+
if isinstance(update, dict):
|
|
215
|
+
update["text"] = text
|
|
216
|
+
|
|
217
|
+
|
|
89
218
|
class StudioRunService:
|
|
90
219
|
"""Persist Studio state without selecting or wrapping a RuntimeAdapter."""
|
|
91
220
|
|
|
@@ -107,29 +236,77 @@ class StudioRunService:
|
|
|
107
236
|
)
|
|
108
237
|
self.runtime_events = runtime_events or RuntimeEventStore(self.session_service)
|
|
109
238
|
self.plugin_runtime = plugin_runtime
|
|
239
|
+
self.schedule_assistant: Any | None = None
|
|
110
240
|
self._active_handles: dict[str, Any] = {}
|
|
241
|
+
self._active_sessions: set[tuple[str, str]] = set()
|
|
111
242
|
self._cancel_flags: dict[str, bool] = {}
|
|
112
243
|
self._control_queues: dict[
|
|
113
244
|
str,
|
|
114
245
|
asyncio.Queue[tuple[str, ResumePayload | None, asyncio.Future[None] | None]],
|
|
115
246
|
] = {}
|
|
116
247
|
self._waiting_modes: dict[str, str] = {}
|
|
248
|
+
self._submitted_interactions: dict[str, set[str]] = {}
|
|
117
249
|
# Interaction resolution appends to one run-level event file. Serialise
|
|
118
250
|
# every decision for that run, not only identical interaction ids, so
|
|
119
251
|
# two concurrently visible cards cannot overwrite each other's receipt.
|
|
120
252
|
self._interaction_locks: dict[str, asyncio.Lock] = {}
|
|
253
|
+
self._recovery_tasks: set[asyncio.Task] = set()
|
|
254
|
+
self._detaching_recoveries = False
|
|
255
|
+
|
|
256
|
+
def has_active_runs(self) -> bool:
|
|
257
|
+
return bool(self._active_sessions or self._active_handles)
|
|
258
|
+
|
|
259
|
+
def is_run_executing(self, run_id: str) -> bool:
|
|
260
|
+
"""该 run 是否仍在本进程内实际执行(内存态判定)。
|
|
121
261
|
|
|
122
|
-
|
|
262
|
+
磁盘 record 停在 RUNNING 的 run 若不在这里,说明是进程崩溃/重启
|
|
263
|
+
遗留的僵尸状态,允许删除会话时将其收尾为 canceled。
|
|
264
|
+
"""
|
|
265
|
+
return run_id in self._active_handles or run_id in self._active_sessions
|
|
266
|
+
|
|
267
|
+
async def reap_stale_running_run(self, run_id: str) -> bool:
|
|
268
|
+
"""把不在执行的僵尸 RUNNING run 收尾为 canceled(幂等)。"""
|
|
269
|
+
from ksadk.events.canonical import RunCanceled
|
|
270
|
+
|
|
271
|
+
record = self.event_store.get(run_id)
|
|
272
|
+
if record.status != RunStatus.RUNNING or self.is_run_executing(run_id):
|
|
273
|
+
return False
|
|
274
|
+
record.status = RunStatus.CANCELLED
|
|
275
|
+
record.error = {"code": "RUN_CANCELLED", "message": "会话删除时回收未完成的运行"}
|
|
276
|
+
record.completed_at = datetime.now(timezone.utc)
|
|
277
|
+
self.event_store.save(record)
|
|
278
|
+
await self.runtime_events.append_one(
|
|
279
|
+
record.session_id,
|
|
280
|
+
RunCanceled(
|
|
281
|
+
schema_version=2,
|
|
282
|
+
event_id=f"evt_reap_{uuid4().hex}",
|
|
283
|
+
seq=len(self.event_store.events(run_id)) + 1,
|
|
284
|
+
timestamp=time.time(),
|
|
285
|
+
run_id=run_id,
|
|
286
|
+
scope_id=run_id,
|
|
287
|
+
source=SourceRef(framework="ksadk", metadata={"reason": "session_deleted"}),
|
|
288
|
+
status="canceled",
|
|
289
|
+
reason="session deleted",
|
|
290
|
+
),
|
|
291
|
+
)
|
|
292
|
+
return True
|
|
293
|
+
|
|
294
|
+
async def recover_interrupted(self, resolve_spec=None) -> None:
|
|
123
295
|
"""Settle local runs left active across a Studio restart.
|
|
124
296
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
297
|
+
Reattach approval-paused providers only when their pinned Build and
|
|
298
|
+
durable-restore capability can be verified. Other in-process runs are
|
|
299
|
+
explicitly interrupted; active execution takeover remains Kernel-owned.
|
|
128
300
|
"""
|
|
129
301
|
active = {RunStatus.RUNNING, RunStatus.WAITING_INPUT}
|
|
130
302
|
for record in self.event_store.list_runs():
|
|
131
303
|
if record.status not in active:
|
|
132
304
|
continue
|
|
305
|
+
if record.status == RunStatus.WAITING_INPUT and resolve_spec is not None:
|
|
306
|
+
from ksadk.studio.provider_recovery import restore_waiting_provider_run
|
|
307
|
+
|
|
308
|
+
if await restore_waiting_provider_run(self, record, resolve_spec):
|
|
309
|
+
continue
|
|
133
310
|
record.status = RunStatus.INTERRUPTED
|
|
134
311
|
record.completed_at = datetime.now(timezone.utc)
|
|
135
312
|
record.error = {
|
|
@@ -142,6 +319,7 @@ class StudioRunService:
|
|
|
142
319
|
"run.interrupted",
|
|
143
320
|
{"reason": "local_studio_restarted", "recoverable": False},
|
|
144
321
|
)
|
|
322
|
+
self._expire_unresolved_interactions(record)
|
|
145
323
|
|
|
146
324
|
async def events(self, run_id: str, *, after: int = 0) -> list[RunEvent]:
|
|
147
325
|
"""Read the durable Studio event timeline for API/SSE replay."""
|
|
@@ -155,6 +333,38 @@ class StudioRunService:
|
|
|
155
333
|
runtime_input: Any = None,
|
|
156
334
|
session_id: str | None = None,
|
|
157
335
|
on_event: Callable[[RunEvent], None] | None = None,
|
|
336
|
+
) -> RunRecord:
|
|
337
|
+
session = session_id or f"ses_{uuid4().hex}"
|
|
338
|
+
key = (spec.agent_id, session)
|
|
339
|
+
# Claim before the first await, including preparation and plugin runs.
|
|
340
|
+
# A second turn must never attach another adapter to the live thread.
|
|
341
|
+
if key in self._active_sessions:
|
|
342
|
+
raise StudioError(
|
|
343
|
+
"SESSION_RUN_ACTIVE",
|
|
344
|
+
"上一轮仍在运行或等待回答,请先完成回答或停止运行。",
|
|
345
|
+
status_code=409,
|
|
346
|
+
details={"sessionId": session},
|
|
347
|
+
)
|
|
348
|
+
self._active_sessions.add(key)
|
|
349
|
+
try:
|
|
350
|
+
return await self._run_owned(
|
|
351
|
+
spec,
|
|
352
|
+
user_input,
|
|
353
|
+
runtime_input=runtime_input,
|
|
354
|
+
session_id=session,
|
|
355
|
+
on_event=on_event,
|
|
356
|
+
)
|
|
357
|
+
finally:
|
|
358
|
+
self._active_sessions.discard(key)
|
|
359
|
+
|
|
360
|
+
async def _run_owned(
|
|
361
|
+
self,
|
|
362
|
+
spec: StudioRunSpec,
|
|
363
|
+
user_input: str,
|
|
364
|
+
*,
|
|
365
|
+
runtime_input: Any,
|
|
366
|
+
session_id: str,
|
|
367
|
+
on_event: Callable[[RunEvent], None] | None,
|
|
158
368
|
) -> RunRecord:
|
|
159
369
|
run_id = f"run_{uuid4().hex}"
|
|
160
370
|
session = session_id or f"ses_{uuid4().hex}"
|
|
@@ -193,7 +403,27 @@ class StudioRunService:
|
|
|
193
403
|
if on_event is not None:
|
|
194
404
|
on_event(created)
|
|
195
405
|
|
|
196
|
-
|
|
406
|
+
if self.schedule_assistant is not None and (
|
|
407
|
+
self.schedule_assistant.matches(user_input)
|
|
408
|
+
or self.schedule_assistant.matches_followup(spec.agent_id, session, user_input)
|
|
409
|
+
):
|
|
410
|
+
handled = await self._schedule_run(record, user_input, on_event=on_event)
|
|
411
|
+
if handled is not None:
|
|
412
|
+
return handled
|
|
413
|
+
|
|
414
|
+
provider_runtime_adapter = (
|
|
415
|
+
spec.plugin_bundle_root is not None
|
|
416
|
+
and (
|
|
417
|
+
runtime_type == "codex"
|
|
418
|
+
or bool(spec.request_config.get("provider_runtime_adapter"))
|
|
419
|
+
)
|
|
420
|
+
)
|
|
421
|
+
# A process-global Codex Kernel can share Agent/cwd with this Build
|
|
422
|
+
# while owning a legacy direct factory. New Builds must retain their
|
|
423
|
+
# exact Provider activation, including when that global Kernel exists.
|
|
424
|
+
kernel_runtime = (
|
|
425
|
+
None if provider_runtime_adapter else self._kernel_runtime_for_spec(spec)
|
|
426
|
+
)
|
|
197
427
|
if kernel_runtime is not None:
|
|
198
428
|
return await self._kernel_run(
|
|
199
429
|
spec,
|
|
@@ -202,7 +432,7 @@ class StudioRunService:
|
|
|
202
432
|
on_event=on_event,
|
|
203
433
|
kernel_runtime=kernel_runtime,
|
|
204
434
|
)
|
|
205
|
-
if spec.plugin_bundle_root is not None:
|
|
435
|
+
if spec.plugin_bundle_root is not None and not provider_runtime_adapter:
|
|
206
436
|
return await self._plugin_run(
|
|
207
437
|
spec,
|
|
208
438
|
user_input,
|
|
@@ -210,12 +440,28 @@ class StudioRunService:
|
|
|
210
440
|
on_event=on_event,
|
|
211
441
|
)
|
|
212
442
|
|
|
443
|
+
return await self._execute_runtime_record(
|
|
444
|
+
spec, user_input, record=record, runtime_input=runtime_input,
|
|
445
|
+
on_event=on_event, prepared_turn=prepared_turn,
|
|
446
|
+
provider_runtime_adapter=provider_runtime_adapter,
|
|
447
|
+
)
|
|
448
|
+
|
|
449
|
+
async def _execute_runtime_record(
|
|
450
|
+
self, spec, user_input, *, record, runtime_input=None, on_event=None,
|
|
451
|
+
prepared_turn=None, provider_runtime_adapter=False, restored_handle=None,
|
|
452
|
+
) -> RunRecord:
|
|
453
|
+
run_id, session = record.id, record.session_id
|
|
454
|
+
runtime_type = record.runtime_type
|
|
213
455
|
started = time.monotonic()
|
|
214
|
-
|
|
215
|
-
|
|
456
|
+
if restored_handle is None:
|
|
457
|
+
record.status = RunStatus.RUNNING
|
|
458
|
+
record.started_at = datetime.now(timezone.utc)
|
|
216
459
|
self.event_store.save(record)
|
|
460
|
+
projected_writer = _ProjectedRunEventWriter(self.event_store, run_id, on_event)
|
|
217
461
|
|
|
218
|
-
async def persist(runtime_event: RuntimeEvent) -> RunEvent:
|
|
462
|
+
async def persist(runtime_event: RuntimeEvent) -> RunEvent | None:
|
|
463
|
+
if _is_private_harness_delta(runtime_event):
|
|
464
|
+
return None
|
|
219
465
|
persisted = await self.runtime_events.append_one(record.session_id, runtime_event)
|
|
220
466
|
event_type, data = project_runtime_event(
|
|
221
467
|
persisted,
|
|
@@ -229,10 +475,7 @@ class StudioRunService:
|
|
|
229
475
|
item_payload = conversation_item.get("payload")
|
|
230
476
|
if isinstance(item_payload, dict):
|
|
231
477
|
item_payload["revision"] = 1
|
|
232
|
-
|
|
233
|
-
if on_event is not None:
|
|
234
|
-
on_event(stored)
|
|
235
|
-
return stored
|
|
478
|
+
return projected_writer.append(event_type, data)
|
|
236
479
|
|
|
237
480
|
handle = None
|
|
238
481
|
completed_text_by_item: dict[tuple[str, str], str] = {}
|
|
@@ -269,17 +512,60 @@ class StudioRunService:
|
|
|
269
512
|
metadata={
|
|
270
513
|
"invocation_id": run_id,
|
|
271
514
|
CONVERSATION_PREPROCESSING_METADATA_KEY: conversation_request,
|
|
272
|
-
**self._native_session_metadata(
|
|
515
|
+
**self._native_session_metadata(
|
|
516
|
+
spec.agent_id,
|
|
517
|
+
session,
|
|
518
|
+
str(
|
|
519
|
+
spec.request_config.get("provider_runtime_type")
|
|
520
|
+
or runtime_type
|
|
521
|
+
),
|
|
522
|
+
spec.build_id,
|
|
523
|
+
),
|
|
273
524
|
},
|
|
274
525
|
)
|
|
275
|
-
|
|
526
|
+
preparation = None
|
|
527
|
+
if provider_runtime_adapter:
|
|
528
|
+
if self.plugin_runtime is None:
|
|
529
|
+
raise StudioError(
|
|
530
|
+
"PLUGIN_RUNTIME_UNAVAILABLE", "Codex Provider Runtime 不可用",
|
|
531
|
+
status_code=503,
|
|
532
|
+
)
|
|
533
|
+
adapter = self.plugin_runtime.kernel_adapter_provider(spec)()
|
|
534
|
+
preparation = RuntimeStartPreparation(context=spec.launch_context, adapter=adapter)
|
|
535
|
+
handle = restored_handle or await self.executor.start(
|
|
536
|
+
spec.launch_context, request, preparation=preparation,
|
|
537
|
+
)
|
|
276
538
|
record.runtime_handle = handle.model_dump(mode="json")
|
|
277
539
|
self.event_store.save(record)
|
|
278
540
|
self._active_handles[run_id] = handle
|
|
279
541
|
while True:
|
|
280
542
|
terminal_seen = False
|
|
281
|
-
should_resume =
|
|
543
|
+
should_resume = restored_handle is not None
|
|
544
|
+
if restored_handle is not None:
|
|
545
|
+
self._waiting_modes[run_id] = "resume"
|
|
546
|
+
restored_handle = None
|
|
282
547
|
async for event in self.executor.stream(handle):
|
|
548
|
+
if (
|
|
549
|
+
isinstance(event, RunInterrupted)
|
|
550
|
+
and event.interaction_id
|
|
551
|
+
and record.status == RunStatus.WAITING_INPUT
|
|
552
|
+
and event.source.framework == "codex"
|
|
553
|
+
):
|
|
554
|
+
# Codex emits this companion event immediately after a
|
|
555
|
+
# live JSON-RPC approval request. The native turn stays
|
|
556
|
+
# attached and must be released through submit; trying
|
|
557
|
+
# to resume it deadlocks that request and later breaks
|
|
558
|
+
# the app-server pipe.
|
|
559
|
+
self._waiting_modes[run_id] = "live"
|
|
560
|
+
continue
|
|
561
|
+
if isinstance(event, InteractionResolved) and event.source.framework == "codex":
|
|
562
|
+
submitted = self._submitted_interactions.get(run_id, set())
|
|
563
|
+
provider_call_id = str(getattr(event.response, "call_id", None) or "")
|
|
564
|
+
if event.interaction_id in submitted or provider_call_id in submitted:
|
|
565
|
+
# submit_interaction atomically persists the public
|
|
566
|
+
# terminal fact and receipt. Ignore the provider's
|
|
567
|
+
# echo so replay never shows the approval twice.
|
|
568
|
+
continue
|
|
283
569
|
await persist(event)
|
|
284
570
|
if self._cancel_flags.get(run_id):
|
|
285
571
|
raise asyncio.CancelledError()
|
|
@@ -288,9 +574,7 @@ class StudioRunService:
|
|
|
288
574
|
elif (
|
|
289
575
|
isinstance(event, ItemUpdated)
|
|
290
576
|
and event.item_kind == "message"
|
|
291
|
-
and item_phases.get(
|
|
292
|
-
(event.scope_id, event.item_id), "final_answer"
|
|
293
|
-
)
|
|
577
|
+
and item_phases.get((event.scope_id, event.item_id), "final_answer")
|
|
294
578
|
== "final_answer"
|
|
295
579
|
):
|
|
296
580
|
text = event.update.text if isinstance(event.update, TextContent) else ""
|
|
@@ -304,9 +588,7 @@ class StudioRunService:
|
|
|
304
588
|
elif (
|
|
305
589
|
isinstance(event, ItemCompleted)
|
|
306
590
|
and event.item_kind == "message"
|
|
307
|
-
and item_phases.get(
|
|
308
|
-
(event.scope_id, event.item_id), "final_answer"
|
|
309
|
-
)
|
|
591
|
+
and item_phases.get((event.scope_id, event.item_id), "final_answer")
|
|
310
592
|
== "final_answer"
|
|
311
593
|
):
|
|
312
594
|
completed_text_by_item[(event.scope_id, event.item_id)] = "".join(
|
|
@@ -428,6 +710,8 @@ class StudioRunService:
|
|
|
428
710
|
fallback_parts = [part for part in streamed_text_by_item.values() if part]
|
|
429
711
|
record.output = "\n\n".join(completed_parts or fallback_parts)
|
|
430
712
|
except asyncio.CancelledError:
|
|
713
|
+
if self._detaching_recoveries and record.status == RunStatus.WAITING_INPUT:
|
|
714
|
+
raise
|
|
431
715
|
cancel_result = "task_cancelled"
|
|
432
716
|
if handle is not None and self.executor.is_attached(handle):
|
|
433
717
|
try:
|
|
@@ -481,6 +765,7 @@ class StudioRunService:
|
|
|
481
765
|
)
|
|
482
766
|
await persist(failure)
|
|
483
767
|
finally:
|
|
768
|
+
projected_writer.flush()
|
|
484
769
|
if handle is not None and self.executor.is_attached(handle):
|
|
485
770
|
try:
|
|
486
771
|
await asyncio.shield(self.executor.close(handle))
|
|
@@ -492,7 +777,13 @@ class StudioRunService:
|
|
|
492
777
|
self._cancel_flags.pop(run_id, None)
|
|
493
778
|
self._control_queues.pop(run_id, None)
|
|
494
779
|
self._waiting_modes.pop(run_id, None)
|
|
495
|
-
|
|
780
|
+
self._submitted_interactions.pop(run_id, None)
|
|
781
|
+
preserve_waiting = (
|
|
782
|
+
self._detaching_recoveries and record.status == RunStatus.WAITING_INPUT
|
|
783
|
+
)
|
|
784
|
+
if not preserve_waiting:
|
|
785
|
+
record.completed_at = datetime.now(timezone.utc)
|
|
786
|
+
self._expire_unresolved_interactions(record, on_event=on_event)
|
|
496
787
|
if runtime_duration_ms is not None:
|
|
497
788
|
record.duration_ms = runtime_duration_ms
|
|
498
789
|
record.duration_source = "runtime"
|
|
@@ -503,6 +794,179 @@ class StudioRunService:
|
|
|
503
794
|
await self._sync_trace(record)
|
|
504
795
|
return record
|
|
505
796
|
|
|
797
|
+
async def _schedule_run(
|
|
798
|
+
self,
|
|
799
|
+
record: RunRecord,
|
|
800
|
+
user_input: str,
|
|
801
|
+
*,
|
|
802
|
+
on_event: Callable[[RunEvent], None] | None,
|
|
803
|
+
) -> RunRecord | None:
|
|
804
|
+
"""Persist real host tool receipts on the canonical conversation stream."""
|
|
805
|
+
started = time.monotonic()
|
|
806
|
+
|
|
807
|
+
def envelope(kind: str) -> dict[str, Any]:
|
|
808
|
+
result = _plugin_event_envelope(record, kind)
|
|
809
|
+
result["source"] = SourceRef(
|
|
810
|
+
framework="ksadk",
|
|
811
|
+
metadata={"runtime": "studio-scheduler", "agent_id": record.agent_id},
|
|
812
|
+
)
|
|
813
|
+
return result
|
|
814
|
+
|
|
815
|
+
async def publish(event: RuntimeEvent) -> None:
|
|
816
|
+
persisted = await self.runtime_events.append_one(record.session_id, event)
|
|
817
|
+
kind, data = project_runtime_event(
|
|
818
|
+
persisted, session_id=record.session_id, public_run_id=record.id
|
|
819
|
+
)
|
|
820
|
+
stored = self.event_store.append(record.id, kind, data)
|
|
821
|
+
if on_event is not None:
|
|
822
|
+
on_event(stored)
|
|
823
|
+
|
|
824
|
+
call_id = None
|
|
825
|
+
try:
|
|
826
|
+
intent = await self.schedule_assistant.plan(
|
|
827
|
+
record.agent_id,
|
|
828
|
+
user_input,
|
|
829
|
+
record.session_id,
|
|
830
|
+
build_id=record.build_id,
|
|
831
|
+
model_name=record.model,
|
|
832
|
+
)
|
|
833
|
+
if self._cancel_flags.get(record.id):
|
|
834
|
+
raise asyncio.CancelledError()
|
|
835
|
+
if intent.action == "ignore":
|
|
836
|
+
return None
|
|
837
|
+
record.status = RunStatus.RUNNING
|
|
838
|
+
record.started_at = datetime.now(timezone.utc)
|
|
839
|
+
record.runtime_handle = {"provider": "studio-scheduler"}
|
|
840
|
+
self.event_store.save(record)
|
|
841
|
+
await publish(RunStarted(**envelope("run.started"), status="running"))
|
|
842
|
+
call_id = f"{record.id}:schedule"
|
|
843
|
+
arguments = intent.model_dump(mode="json", exclude_none=True)
|
|
844
|
+
await publish(
|
|
845
|
+
ItemStarted(
|
|
846
|
+
**envelope("tool.started"),
|
|
847
|
+
item_id=call_id,
|
|
848
|
+
item_kind="tool_call",
|
|
849
|
+
initial=ContentSnapshot(
|
|
850
|
+
parts=(
|
|
851
|
+
ToolCallContent(
|
|
852
|
+
part_id=f"{call_id}:call",
|
|
853
|
+
call_id=call_id,
|
|
854
|
+
name="studio_schedule",
|
|
855
|
+
arguments=arguments,
|
|
856
|
+
),
|
|
857
|
+
)
|
|
858
|
+
),
|
|
859
|
+
)
|
|
860
|
+
)
|
|
861
|
+
result = await self.schedule_assistant.execute(
|
|
862
|
+
record.agent_id, record.session_id, intent
|
|
863
|
+
)
|
|
864
|
+
await publish(
|
|
865
|
+
ItemCompleted(
|
|
866
|
+
**envelope("tool.completed"),
|
|
867
|
+
item_id=call_id,
|
|
868
|
+
item_kind="tool_call",
|
|
869
|
+
snapshot=ContentSnapshot(
|
|
870
|
+
parts=(
|
|
871
|
+
ToolResultContent(
|
|
872
|
+
part_id=f"{call_id}:result", call_id=call_id, result=result
|
|
873
|
+
),
|
|
874
|
+
)
|
|
875
|
+
),
|
|
876
|
+
)
|
|
877
|
+
)
|
|
878
|
+
output = result["message"]
|
|
879
|
+
item_id, part_id = f"{record.id}:assistant", f"{record.id}:text"
|
|
880
|
+
await publish(
|
|
881
|
+
ItemStarted(
|
|
882
|
+
**envelope("item.started"),
|
|
883
|
+
item_id=item_id,
|
|
884
|
+
item_kind="message",
|
|
885
|
+
phase="final_answer",
|
|
886
|
+
)
|
|
887
|
+
)
|
|
888
|
+
await publish(
|
|
889
|
+
ItemUpdated(
|
|
890
|
+
**envelope("item.updated"),
|
|
891
|
+
item_id=item_id,
|
|
892
|
+
item_kind="message",
|
|
893
|
+
op="append",
|
|
894
|
+
update=TextContent(part_id=part_id, text=output),
|
|
895
|
+
)
|
|
896
|
+
)
|
|
897
|
+
await publish(
|
|
898
|
+
ItemCompleted(
|
|
899
|
+
**envelope("item.completed"),
|
|
900
|
+
item_id=item_id,
|
|
901
|
+
item_kind="message",
|
|
902
|
+
snapshot=ContentSnapshot(parts=(TextContent(part_id=part_id, text=output),)),
|
|
903
|
+
)
|
|
904
|
+
)
|
|
905
|
+
if intent._usage is not None and intent._usage.reported:
|
|
906
|
+
record.usage = intent._usage.model_copy(update={"source": "studio-scheduler"})
|
|
907
|
+
await publish(
|
|
908
|
+
UsageReported(
|
|
909
|
+
**envelope("usage.reported"),
|
|
910
|
+
input_tokens=record.usage.input_tokens,
|
|
911
|
+
output_tokens=record.usage.output_tokens,
|
|
912
|
+
total_tokens=record.usage.total_tokens,
|
|
913
|
+
cached_tokens=record.usage.cached_input_tokens,
|
|
914
|
+
reasoning_tokens=record.usage.reasoning_output_tokens,
|
|
915
|
+
)
|
|
916
|
+
)
|
|
917
|
+
await publish(
|
|
918
|
+
RunCompleted(
|
|
919
|
+
**envelope("run.completed"),
|
|
920
|
+
status="completed",
|
|
921
|
+
output_refs=(OutputRef(scope_id=record.id, item_id=item_id, part_id=part_id),),
|
|
922
|
+
)
|
|
923
|
+
)
|
|
924
|
+
record.output = output
|
|
925
|
+
record.status = RunStatus.COMPLETED
|
|
926
|
+
except asyncio.CancelledError:
|
|
927
|
+
record.status = RunStatus.CANCELLED
|
|
928
|
+
record.completed_at = datetime.now(timezone.utc)
|
|
929
|
+
self._cancel_flags.pop(record.id, None)
|
|
930
|
+
await publish(
|
|
931
|
+
RunCanceled(**envelope("run.canceled"), status="canceled", reason="cancelled")
|
|
932
|
+
)
|
|
933
|
+
self.event_store.save(record)
|
|
934
|
+
raise
|
|
935
|
+
except Exception as error:
|
|
936
|
+
code = str(getattr(error, "code", "SCHEDULE_REQUEST_FAILED"))
|
|
937
|
+
record.status = RunStatus.FAILED
|
|
938
|
+
record.error = {"code": code, "message": str(error)}
|
|
939
|
+
if call_id is not None:
|
|
940
|
+
await publish(
|
|
941
|
+
ItemFailed(
|
|
942
|
+
**envelope("tool.failed"),
|
|
943
|
+
item_id=call_id,
|
|
944
|
+
item_kind="tool_call",
|
|
945
|
+
error=ErrorInfo(
|
|
946
|
+
code=code,
|
|
947
|
+
message=str(error),
|
|
948
|
+
source="studio-scheduler",
|
|
949
|
+
scope_id=record.id,
|
|
950
|
+
),
|
|
951
|
+
)
|
|
952
|
+
)
|
|
953
|
+
await publish(
|
|
954
|
+
RunFailed(
|
|
955
|
+
**envelope("run.failed"),
|
|
956
|
+
status="failed",
|
|
957
|
+
error=ErrorInfo(
|
|
958
|
+
code=code, message=str(error), source="studio-scheduler", scope_id=record.id
|
|
959
|
+
),
|
|
960
|
+
)
|
|
961
|
+
)
|
|
962
|
+
record.completed_at = datetime.now(timezone.utc)
|
|
963
|
+
record.duration_ms = int((time.monotonic() - started) * 1000)
|
|
964
|
+
record.duration_source = "studio"
|
|
965
|
+
self._cancel_flags.pop(record.id, None)
|
|
966
|
+
self.event_store.save(record)
|
|
967
|
+
await self._sync_trace(record)
|
|
968
|
+
return record
|
|
969
|
+
|
|
506
970
|
async def _sync_trace(self, record: RunRecord) -> None:
|
|
507
971
|
self.event_store.trace_store.sync(record, await self.events(record.id))
|
|
508
972
|
|
|
@@ -531,18 +995,61 @@ class StudioRunService:
|
|
|
531
995
|
"activationKey": record.session_id,
|
|
532
996
|
}
|
|
533
997
|
self.event_store.save(record)
|
|
998
|
+
projected_writer = _ProjectedRunEventWriter(self.event_store, record.id, on_event)
|
|
534
999
|
rows_before = await self.session_service.get_events(record.session_id)
|
|
535
1000
|
after_seq = max((int(row.seq_id or 0) for row in rows_before), default=0)
|
|
1001
|
+
published_seq = after_seq
|
|
536
1002
|
|
|
537
1003
|
async def publish(events: list[RuntimeEvent]) -> None:
|
|
1004
|
+
nonlocal published_seq
|
|
538
1005
|
for runtime_event in events:
|
|
1006
|
+
# Harness keeps token-level child output in the canonical
|
|
1007
|
+
# session trace for diagnostics. It is deliberately not part
|
|
1008
|
+
# of Studio's public run timeline: the activity projection
|
|
1009
|
+
# never consumes these private deltas, and persisting each one
|
|
1010
|
+
# rewrites the growing run JSON file. Long-running delegated
|
|
1011
|
+
# research can otherwise turn a few useful activity rows into
|
|
1012
|
+
# thousands of O(n²) file writes and make the page appear
|
|
1013
|
+
# frozen. Advance the cursor so final catch-up will not replay
|
|
1014
|
+
# an omitted delta.
|
|
1015
|
+
if _is_private_harness_delta(runtime_event):
|
|
1016
|
+
published_seq = max(published_seq, int(runtime_event.seq or 0))
|
|
1017
|
+
continue
|
|
539
1018
|
event_type, data = project_runtime_event(
|
|
540
1019
|
runtime_event,
|
|
541
1020
|
session_id=record.session_id,
|
|
542
1021
|
)
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
1022
|
+
projected_writer.append(event_type, data)
|
|
1023
|
+
published_seq = max(published_seq, int(runtime_event.seq or 0))
|
|
1024
|
+
|
|
1025
|
+
async def tail_live_events() -> None:
|
|
1026
|
+
# Kernel worker 会把 harness 的增量事实(TEXT_DELTA/ItemUpdated 等)
|
|
1027
|
+
# 在执行期间逐条写入 canonical session store;这里实时转发给 SSE
|
|
1028
|
+
# 订阅端,避免整轮结束后一次性爆发。终态事件会自然结束订阅。
|
|
1029
|
+
async for event in self.runtime_events.subscribe_run(
|
|
1030
|
+
record.session_id,
|
|
1031
|
+
record.id,
|
|
1032
|
+
after_seq=published_seq,
|
|
1033
|
+
poll_interval=0.1,
|
|
1034
|
+
timeout=600,
|
|
1035
|
+
):
|
|
1036
|
+
try:
|
|
1037
|
+
await publish([event])
|
|
1038
|
+
except asyncio.CancelledError:
|
|
1039
|
+
raise
|
|
1040
|
+
except Exception:
|
|
1041
|
+
logger.exception(
|
|
1042
|
+
"failed to publish live plugin event for run %s", record.id
|
|
1043
|
+
)
|
|
1044
|
+
|
|
1045
|
+
tail_task: asyncio.Task[None] | None = None
|
|
1046
|
+
|
|
1047
|
+
async def stop_tail() -> None:
|
|
1048
|
+
nonlocal tail_task
|
|
1049
|
+
if tail_task is not None:
|
|
1050
|
+
tail_task.cancel()
|
|
1051
|
+
await asyncio.gather(tail_task, return_exceptions=True)
|
|
1052
|
+
tail_task = None
|
|
546
1053
|
|
|
547
1054
|
try:
|
|
548
1055
|
if self.plugin_runtime is None:
|
|
@@ -561,18 +1068,22 @@ class StudioRunService:
|
|
|
561
1068
|
}.items()
|
|
562
1069
|
if value not in {None, ""}
|
|
563
1070
|
}
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
1071
|
+
tail_task = asyncio.create_task(tail_live_events())
|
|
1072
|
+
try:
|
|
1073
|
+
result = await self.plugin_runtime.execute(
|
|
1074
|
+
spec,
|
|
1075
|
+
{
|
|
1076
|
+
"user_id": "local-user",
|
|
1077
|
+
"session_id": record.session_id,
|
|
1078
|
+
"invocation_id": record.id,
|
|
1079
|
+
"messages": [{"role": "user", "content": user_input}],
|
|
1080
|
+
"model": spec.model,
|
|
1081
|
+
"request_metadata": request_metadata,
|
|
1082
|
+
},
|
|
1083
|
+
session_id=record.session_id,
|
|
1084
|
+
)
|
|
1085
|
+
finally:
|
|
1086
|
+
await stop_tail()
|
|
576
1087
|
if result.session_id != record.session_id:
|
|
577
1088
|
raise StudioError(
|
|
578
1089
|
"PLUGIN_SESSION_MISMATCH",
|
|
@@ -585,7 +1096,7 @@ class StudioRunService:
|
|
|
585
1096
|
)
|
|
586
1097
|
canonical = await self._plugin_events_after(
|
|
587
1098
|
record.session_id,
|
|
588
|
-
after_seq=
|
|
1099
|
+
after_seq=published_seq,
|
|
589
1100
|
run_id=record.id,
|
|
590
1101
|
)
|
|
591
1102
|
if not canonical:
|
|
@@ -599,6 +1110,7 @@ class StudioRunService:
|
|
|
599
1110
|
record.status = RunStatus.COMPLETED
|
|
600
1111
|
_apply_plugin_usage(record, result.usage)
|
|
601
1112
|
except asyncio.CancelledError:
|
|
1113
|
+
await stop_tail()
|
|
602
1114
|
record.status = RunStatus.CANCELLED
|
|
603
1115
|
record.error = {"code": "RUN_CANCELLED", "message": "运行已取消"}
|
|
604
1116
|
cancelled = await self.runtime_events.append_one(
|
|
@@ -612,12 +1124,13 @@ class StudioRunService:
|
|
|
612
1124
|
await publish([cancelled])
|
|
613
1125
|
raise
|
|
614
1126
|
except Exception as exc: # noqa: BLE001 - plugin boundary is typed below
|
|
1127
|
+
await stop_tail()
|
|
615
1128
|
record.status = RunStatus.FAILED
|
|
616
1129
|
code = str(getattr(exc, "code", "PLUGIN_RUNTIME_FAILED"))
|
|
617
1130
|
record.error = {"code": code, "message": str(exc)}
|
|
618
1131
|
canonical = await self._plugin_events_after(
|
|
619
1132
|
record.session_id,
|
|
620
|
-
after_seq=
|
|
1133
|
+
after_seq=published_seq,
|
|
621
1134
|
run_id=record.id,
|
|
622
1135
|
)
|
|
623
1136
|
if canonical:
|
|
@@ -638,6 +1151,7 @@ class StudioRunService:
|
|
|
638
1151
|
)
|
|
639
1152
|
await publish([failed])
|
|
640
1153
|
finally:
|
|
1154
|
+
projected_writer.flush()
|
|
641
1155
|
record.completed_at = datetime.now(timezone.utc)
|
|
642
1156
|
record.duration_ms = int((time.monotonic() - started) * 1000)
|
|
643
1157
|
record.duration_source = "studio"
|
|
@@ -672,9 +1186,7 @@ class StudioRunService:
|
|
|
672
1186
|
) -> list[RuntimeEvent]:
|
|
673
1187
|
item_id = f"{record.id}:assistant"
|
|
674
1188
|
part_id = f"{record.id}:text"
|
|
675
|
-
snapshot = ContentSnapshot(
|
|
676
|
-
parts=(TextContent(part_id=part_id, text=output_text),)
|
|
677
|
-
)
|
|
1189
|
+
snapshot = ContentSnapshot(parts=(TextContent(part_id=part_id, text=output_text),))
|
|
678
1190
|
events: list[RuntimeEvent] = [
|
|
679
1191
|
RunStarted(
|
|
680
1192
|
**_plugin_event_envelope(record, "run.started"),
|
|
@@ -725,10 +1237,7 @@ class StudioRunService:
|
|
|
725
1237
|
),
|
|
726
1238
|
)
|
|
727
1239
|
)
|
|
728
|
-
return [
|
|
729
|
-
await self.runtime_events.append_one(record.session_id, event)
|
|
730
|
-
for event in events
|
|
731
|
-
]
|
|
1240
|
+
return [await self.runtime_events.append_one(record.session_id, event) for event in events]
|
|
732
1241
|
|
|
733
1242
|
async def _kernel_run(
|
|
734
1243
|
self,
|
|
@@ -764,9 +1273,7 @@ class StudioRunService:
|
|
|
764
1273
|
session_id=record.session_id,
|
|
765
1274
|
operations=("enqueue",),
|
|
766
1275
|
)
|
|
767
|
-
idempotency_key = str(
|
|
768
|
-
(spec.request_config or {}).get("idempotency_key") or record.id
|
|
769
|
-
)
|
|
1276
|
+
idempotency_key = str((spec.request_config or {}).get("idempotency_key") or record.id)
|
|
770
1277
|
command = _kernel_ingress.map_studio_request(
|
|
771
1278
|
session_id=record.session_id,
|
|
772
1279
|
idempotency_key=idempotency_key,
|
|
@@ -774,9 +1281,7 @@ class StudioRunService:
|
|
|
774
1281
|
run_id=record.id,
|
|
775
1282
|
trusted=trusted,
|
|
776
1283
|
)
|
|
777
|
-
receipt = await _kernel_ingress.submit_command(
|
|
778
|
-
command, permit=trusted.permit
|
|
779
|
-
)
|
|
1284
|
+
receipt = await _kernel_ingress.submit_command(command, permit=trusted.permit)
|
|
780
1285
|
if receipt.status not in ("accepted", "duplicate"):
|
|
781
1286
|
record.status = RunStatus.FAILED
|
|
782
1287
|
record.error = {
|
|
@@ -859,17 +1364,13 @@ class StudioRunService:
|
|
|
859
1364
|
if active_project != build_project:
|
|
860
1365
|
return None
|
|
861
1366
|
defaults = getattr(runtime.config, "start_request_defaults", {}) or {}
|
|
862
|
-
bound_agent_id = str(
|
|
863
|
-
defaults.get("agent_id") or runtime.config.agent_instance_id
|
|
864
|
-
).strip()
|
|
1367
|
+
bound_agent_id = str(defaults.get("agent_id") or runtime.config.agent_instance_id).strip()
|
|
865
1368
|
if bound_agent_id != spec.agent_id:
|
|
866
1369
|
return None
|
|
867
1370
|
declared_instance = str(
|
|
868
1371
|
(spec.launch_context.config or {}).get("agent_instance_id") or ""
|
|
869
1372
|
).strip()
|
|
870
|
-
if declared_instance and declared_instance != str(
|
|
871
|
-
runtime.config.agent_instance_id
|
|
872
|
-
):
|
|
1373
|
+
if declared_instance and declared_instance != str(runtime.config.agent_instance_id):
|
|
873
1374
|
return None
|
|
874
1375
|
return runtime
|
|
875
1376
|
|
|
@@ -998,11 +1499,8 @@ class StudioRunService:
|
|
|
998
1499
|
"同一幂等键不能提交不同内容",
|
|
999
1500
|
status_code=409,
|
|
1000
1501
|
)
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
record.status = RunStatus.RUNNING
|
|
1004
|
-
self.event_store.save(record)
|
|
1005
|
-
self._waiting_modes.pop(run_id, None)
|
|
1502
|
+
# A receipt replay is read-only. The run may already be waiting
|
|
1503
|
+
# for a different interaction; never clear that newer wait state.
|
|
1006
1504
|
return dict(prior.data.get("receipt") or {})
|
|
1007
1505
|
|
|
1008
1506
|
interaction = next(
|
|
@@ -1039,10 +1537,51 @@ class StudioRunService:
|
|
|
1039
1537
|
status_code=409,
|
|
1040
1538
|
)
|
|
1041
1539
|
kind = str(interaction.data.get("kind") or "form")
|
|
1042
|
-
|
|
1540
|
+
if kind != "approval" and name not in {"cancel", "skip"}:
|
|
1541
|
+
schema = interaction.data.get("inputSchema") or {}
|
|
1542
|
+
errors = list(Draft202012Validator(schema).iter_errors(data))
|
|
1543
|
+
if errors:
|
|
1544
|
+
raise StudioError(
|
|
1545
|
+
"INTERACTION_RESPONSE_INVALID",
|
|
1546
|
+
"请填写必填问题后再提交。",
|
|
1547
|
+
status_code=422,
|
|
1548
|
+
)
|
|
1549
|
+
provider_call_id = str(
|
|
1550
|
+
interaction.data.get("callId") or interaction.data.get("call_id") or ""
|
|
1551
|
+
)
|
|
1552
|
+
if kind == "approval" and not provider_call_id:
|
|
1553
|
+
approval_request = next(
|
|
1554
|
+
(
|
|
1555
|
+
event
|
|
1556
|
+
for event in reversed(events)
|
|
1557
|
+
if event.type == "approval.requested"
|
|
1558
|
+
and str(event.data.get("approvalId") or event.data.get("interactionId") or "")
|
|
1559
|
+
== interaction_id
|
|
1560
|
+
),
|
|
1561
|
+
None,
|
|
1562
|
+
)
|
|
1563
|
+
if approval_request is not None:
|
|
1564
|
+
provider_call_id = str(
|
|
1565
|
+
approval_request.data.get("callId")
|
|
1566
|
+
or approval_request.data.get("call_id")
|
|
1567
|
+
or ""
|
|
1568
|
+
)
|
|
1569
|
+
provider_call_id = provider_call_id or interaction_id
|
|
1570
|
+
submitted = self._submitted_interactions.setdefault(run_id, set())
|
|
1571
|
+
submitted.update({interaction_id, provider_call_id})
|
|
1572
|
+
decision = str(data.get("decision") or name)
|
|
1573
|
+
if kind == "approval" and record.runtime_type == "codex":
|
|
1574
|
+
# The shared Web action is named `reject`; Codex app-server calls
|
|
1575
|
+
# the same decision `deny`/`decline`. Do not let a UI alias fall
|
|
1576
|
+
# through as an unsupported native approval decision.
|
|
1577
|
+
decision = {
|
|
1578
|
+
"reject": "deny",
|
|
1579
|
+
"rejected": "deny",
|
|
1580
|
+
}.get(decision.lower(), decision)
|
|
1581
|
+
payload_data = {**data, "decision": decision}
|
|
1043
1582
|
payload = ResumePayload(
|
|
1044
1583
|
kind="approval_decision" if kind == "approval" else "hitl_answer",
|
|
1045
|
-
call_id=
|
|
1584
|
+
call_id=provider_call_id,
|
|
1046
1585
|
data=payload_data,
|
|
1047
1586
|
)
|
|
1048
1587
|
mode = self._waiting_modes.get(run_id)
|
|
@@ -1051,8 +1590,19 @@ class StudioRunService:
|
|
|
1051
1590
|
if handle is None or not self.executor.is_attached(handle):
|
|
1052
1591
|
raise StudioError("INTERACTION_EXPIRED", "运行时交互已失效", status_code=409)
|
|
1053
1592
|
try:
|
|
1054
|
-
await
|
|
1593
|
+
await asyncio.wait_for(
|
|
1594
|
+
self.executor.submit(handle, payload),
|
|
1595
|
+
timeout=_INTERACTION_SUBMIT_TIMEOUT_SECONDS,
|
|
1596
|
+
)
|
|
1597
|
+
except TimeoutError as exc:
|
|
1598
|
+
submitted.difference_update({interaction_id, provider_call_id})
|
|
1599
|
+
raise StudioError(
|
|
1600
|
+
"INTERACTION_SUBMIT_FAILED",
|
|
1601
|
+
"运行时未及时确认交互提交,请重试",
|
|
1602
|
+
status_code=409,
|
|
1603
|
+
) from exc
|
|
1055
1604
|
except Exception as exc: # noqa: BLE001 - provider errors are user-safe here
|
|
1605
|
+
submitted.difference_update({interaction_id, provider_call_id})
|
|
1056
1606
|
raise StudioError(
|
|
1057
1607
|
"INTERACTION_SUBMIT_FAILED",
|
|
1058
1608
|
str(exc),
|
|
@@ -1065,22 +1615,51 @@ class StudioRunService:
|
|
|
1065
1615
|
submit_ack = asyncio.get_running_loop().create_future()
|
|
1066
1616
|
queue.put_nowait(("resume", payload, submit_ack))
|
|
1067
1617
|
try:
|
|
1068
|
-
await
|
|
1618
|
+
await asyncio.wait_for(
|
|
1619
|
+
submit_ack,
|
|
1620
|
+
timeout=_INTERACTION_SUBMIT_TIMEOUT_SECONDS,
|
|
1621
|
+
)
|
|
1622
|
+
except TimeoutError as exc:
|
|
1623
|
+
submitted.difference_update({interaction_id, provider_call_id})
|
|
1624
|
+
raise StudioError(
|
|
1625
|
+
"INTERACTION_SUBMIT_FAILED",
|
|
1626
|
+
"运行时未及时确认交互提交,请重试",
|
|
1627
|
+
status_code=409,
|
|
1628
|
+
) from exc
|
|
1069
1629
|
except Exception as exc: # noqa: BLE001 - provider errors are user-safe here
|
|
1630
|
+
submitted.difference_update({interaction_id, provider_call_id})
|
|
1070
1631
|
raise StudioError(
|
|
1071
1632
|
"INTERACTION_SUBMIT_FAILED",
|
|
1072
1633
|
str(exc),
|
|
1073
1634
|
status_code=409,
|
|
1074
1635
|
) from exc
|
|
1075
1636
|
else:
|
|
1637
|
+
submitted.difference_update({interaction_id, provider_call_id})
|
|
1076
1638
|
raise StudioError("INTERACTION_EXPIRED", "运行时交互已失效", status_code=409)
|
|
1077
1639
|
|
|
1078
1640
|
next_revision = revision + 1
|
|
1641
|
+
normalized_action = name.lower()
|
|
1642
|
+
if normalized_action in {"approve", "approved", "accept", "accepted"}:
|
|
1643
|
+
outcome = "approved"
|
|
1644
|
+
response_summary = "已同意"
|
|
1645
|
+
elif normalized_action in {"reject", "rejected", "deny", "denied", "decline"}:
|
|
1646
|
+
outcome = "rejected"
|
|
1647
|
+
response_summary = "已拒绝"
|
|
1648
|
+
elif normalized_action in {"cancel", "cancelled", "canceled"}:
|
|
1649
|
+
outcome = "cancelled"
|
|
1650
|
+
response_summary = "已反馈给 Agent" if data.get("feedback") else "已取消"
|
|
1651
|
+
else:
|
|
1652
|
+
outcome = "submitted"
|
|
1653
|
+
response_summary = "已提交"
|
|
1079
1654
|
resolved_data = {
|
|
1080
1655
|
"runId": run_id,
|
|
1081
1656
|
"interactionId": interaction_id,
|
|
1082
|
-
"callId":
|
|
1657
|
+
"callId": provider_call_id,
|
|
1083
1658
|
"name": name,
|
|
1659
|
+
"action": name,
|
|
1660
|
+
"outcome": outcome,
|
|
1661
|
+
"actor": "user",
|
|
1662
|
+
"responseSummary": response_summary,
|
|
1084
1663
|
"data": data,
|
|
1085
1664
|
"revision": next_revision,
|
|
1086
1665
|
"expectedRevision": expected_revision,
|
|
@@ -1092,6 +1671,7 @@ class StudioRunService:
|
|
|
1092
1671
|
interaction.data.get("surfaceId") or interaction.data.get("surface_id") or ""
|
|
1093
1672
|
),
|
|
1094
1673
|
"interactionId": interaction_id,
|
|
1674
|
+
"callId": provider_call_id,
|
|
1095
1675
|
"actionId": f"action-{interaction_id}",
|
|
1096
1676
|
"name": name,
|
|
1097
1677
|
"data": data,
|
|
@@ -1111,6 +1691,65 @@ class StudioRunService:
|
|
|
1111
1691
|
self._waiting_modes.pop(run_id, None)
|
|
1112
1692
|
return receipt
|
|
1113
1693
|
|
|
1694
|
+
def _expire_unresolved_interactions(
|
|
1695
|
+
self,
|
|
1696
|
+
record: RunRecord,
|
|
1697
|
+
*,
|
|
1698
|
+
on_event: Callable[[RunEvent], None] | None = None,
|
|
1699
|
+
) -> None:
|
|
1700
|
+
"""Close orphaned cards whenever their owning run is terminal."""
|
|
1701
|
+
|
|
1702
|
+
if record.status not in {
|
|
1703
|
+
RunStatus.COMPLETED,
|
|
1704
|
+
RunStatus.FAILED,
|
|
1705
|
+
RunStatus.CANCELLED,
|
|
1706
|
+
RunStatus.INTERRUPTED,
|
|
1707
|
+
RunStatus.TIMED_OUT,
|
|
1708
|
+
}:
|
|
1709
|
+
return
|
|
1710
|
+
events = self.event_store.events(record.id)
|
|
1711
|
+
closed_ids = {
|
|
1712
|
+
str(event.data.get("interactionId") or event.data.get("approvalId") or "")
|
|
1713
|
+
for event in events
|
|
1714
|
+
if event.type
|
|
1715
|
+
in {
|
|
1716
|
+
"a2ui.action",
|
|
1717
|
+
"approval.resolved",
|
|
1718
|
+
"interaction.resolved",
|
|
1719
|
+
"interaction.cancelled",
|
|
1720
|
+
"interaction.expired",
|
|
1721
|
+
}
|
|
1722
|
+
}
|
|
1723
|
+
pending: dict[str, RunEvent] = {}
|
|
1724
|
+
for event in events:
|
|
1725
|
+
if event.type != "a2ui.interaction":
|
|
1726
|
+
continue
|
|
1727
|
+
interaction_id = str(
|
|
1728
|
+
event.data.get("interactionId") or event.data.get("interaction_id") or ""
|
|
1729
|
+
)
|
|
1730
|
+
if interaction_id and interaction_id not in closed_ids:
|
|
1731
|
+
pending[interaction_id] = event
|
|
1732
|
+
for interaction_id, requested in pending.items():
|
|
1733
|
+
data = requested.data
|
|
1734
|
+
expired = self.event_store.append(
|
|
1735
|
+
record.id,
|
|
1736
|
+
"interaction.expired",
|
|
1737
|
+
{
|
|
1738
|
+
"runId": record.id,
|
|
1739
|
+
"interactionId": interaction_id,
|
|
1740
|
+
"approvalId": interaction_id,
|
|
1741
|
+
"callId": str(data.get("callId") or data.get("call_id") or ""),
|
|
1742
|
+
"kind": str(data.get("kind") or "form"),
|
|
1743
|
+
"title": data.get("title"),
|
|
1744
|
+
"message": data.get("message"),
|
|
1745
|
+
"detail": data.get("detail"),
|
|
1746
|
+
"outcome": "expired",
|
|
1747
|
+
"revision": _interaction_revision(data) + 1,
|
|
1748
|
+
},
|
|
1749
|
+
)
|
|
1750
|
+
if on_event is not None:
|
|
1751
|
+
on_event(expired)
|
|
1752
|
+
|
|
1114
1753
|
async def _capture_pcm_evidence(
|
|
1115
1754
|
self,
|
|
1116
1755
|
record: RunRecord,
|
|
@@ -1255,7 +1894,11 @@ class StudioRunService:
|
|
|
1255
1894
|
"runId": record.id,
|
|
1256
1895
|
"surfaceId": surface_id,
|
|
1257
1896
|
"interactionId": approval_id,
|
|
1897
|
+
"callId": event.request.call_id or approval_id,
|
|
1258
1898
|
"kind": "approval",
|
|
1899
|
+
"title": "需要审批",
|
|
1900
|
+
"message": "此次操作需要确认后才能继续。",
|
|
1901
|
+
"detail": detail,
|
|
1259
1902
|
"revision": 1,
|
|
1260
1903
|
"inputSchema": {
|
|
1261
1904
|
"type": "object",
|
|
@@ -1284,7 +1927,41 @@ class StudioRunService:
|
|
|
1284
1927
|
if previous.agent_id != agent_id or previous.status != RunStatus.COMPLETED:
|
|
1285
1928
|
continue
|
|
1286
1929
|
messages.append({"role": "user", "content": previous.input})
|
|
1287
|
-
|
|
1930
|
+
assistant_content = previous.output
|
|
1931
|
+
if previous.runtime_type == "harness":
|
|
1932
|
+
projection = project_run_activities(
|
|
1933
|
+
previous,
|
|
1934
|
+
self.event_store.events(previous.id),
|
|
1935
|
+
)
|
|
1936
|
+
children = [
|
|
1937
|
+
activity
|
|
1938
|
+
for activity in projection.get("activities", [])
|
|
1939
|
+
if activity.get("kind") == "subagent"
|
|
1940
|
+
][:8]
|
|
1941
|
+
if children:
|
|
1942
|
+
status_text = {
|
|
1943
|
+
"completed": "已完成",
|
|
1944
|
+
"failed": "失败",
|
|
1945
|
+
"cancelled": "已取消",
|
|
1946
|
+
"running": "仍在运行",
|
|
1947
|
+
"unknown": "状态未确认",
|
|
1948
|
+
}
|
|
1949
|
+
facts = ";".join(
|
|
1950
|
+
f"子智能体“{child.get('label') or '未命名'}”"
|
|
1951
|
+
f"{status_text.get(str(child.get('status')), '状态未确认')}"
|
|
1952
|
+
for child in children
|
|
1953
|
+
)
|
|
1954
|
+
# Lifecycle evidence belongs to the completed assistant turn.
|
|
1955
|
+
# A standalone system message inserted between historical turns
|
|
1956
|
+
# can outweigh the next user request on some providers and make
|
|
1957
|
+
# a new turn answer an old child result instead (conversation
|
|
1958
|
+
# "cross-talk"). Keep the evidence attached to the turn it
|
|
1959
|
+
# describes so the newest user message remains authoritative.
|
|
1960
|
+
assistant_content = (
|
|
1961
|
+
f"{assistant_content}\n\n"
|
|
1962
|
+
f"[Harness 执行记录] {facts}。"
|
|
1963
|
+
)
|
|
1964
|
+
messages.append({"role": "assistant", "content": assistant_content})
|
|
1288
1965
|
messages.append({"role": "user", "content": user_input})
|
|
1289
1966
|
return messages
|
|
1290
1967
|
|
|
@@ -1293,16 +1970,30 @@ class StudioRunService:
|
|
|
1293
1970
|
agent_id: str,
|
|
1294
1971
|
session_id: str,
|
|
1295
1972
|
runtime_type: str,
|
|
1973
|
+
build_id: str,
|
|
1296
1974
|
) -> dict[str, str]:
|
|
1297
1975
|
if runtime_type != "codex":
|
|
1298
1976
|
return {}
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1977
|
+
runs = self.event_store.list_runs(session_id=session_id, agent_id=agent_id)
|
|
1978
|
+
thread_builds: dict[str, str] = {}
|
|
1979
|
+
for previous in runs:
|
|
1980
|
+
native_ref = previous.runtime_handle.get("native_ref") or {}
|
|
1981
|
+
if isinstance(native_ref, dict) and native_ref.get("thread_id"):
|
|
1982
|
+
# Older Studio versions also saved failed cross-build resumes.
|
|
1983
|
+
# The first occurrence identifies the thread's actual home.
|
|
1984
|
+
thread_builds.setdefault(str(native_ref["thread_id"]), previous.build_id)
|
|
1985
|
+
for previous in reversed(runs):
|
|
1986
|
+
# Each immutable build has its own CODEX_HOME and plugin snapshot.
|
|
1987
|
+
# Starting a thread in that home applies the new capabilities; the
|
|
1988
|
+
# transport-neutral history above preserves the Studio conversation.
|
|
1989
|
+
if previous.build_id != build_id:
|
|
1990
|
+
return {}
|
|
1302
1991
|
native_ref = previous.runtime_handle.get("native_ref")
|
|
1303
1992
|
native_ref = native_ref if isinstance(native_ref, dict) else {}
|
|
1304
1993
|
thread_id = str(native_ref.get("thread_id") or "")
|
|
1305
1994
|
if thread_id:
|
|
1995
|
+
if thread_builds[thread_id] != build_id:
|
|
1996
|
+
return {}
|
|
1306
1997
|
return {"thread_id": thread_id}
|
|
1307
1998
|
return {}
|
|
1308
1999
|
|
|
@@ -1340,17 +2031,13 @@ def _normalized_plugin_usage(usage: Mapping[str, Any]) -> dict[str, Any]:
|
|
|
1340
2031
|
|
|
1341
2032
|
normalized = {
|
|
1342
2033
|
"input_tokens": number("input_tokens", "inputTokens", "prompt_tokens"),
|
|
1343
|
-
"output_tokens": number(
|
|
1344
|
-
"output_tokens", "outputTokens", "completion_tokens"
|
|
1345
|
-
),
|
|
2034
|
+
"output_tokens": number("output_tokens", "outputTokens", "completion_tokens"),
|
|
1346
2035
|
"total_tokens": number("total_tokens", "totalTokens"),
|
|
1347
2036
|
"cached_tokens": number("cached_tokens", "cachedTokens"),
|
|
1348
2037
|
"reasoning_tokens": number("reasoning_tokens", "reasoningTokens"),
|
|
1349
2038
|
}
|
|
1350
2039
|
if not normalized["total_tokens"]:
|
|
1351
|
-
normalized["total_tokens"] =
|
|
1352
|
-
normalized["input_tokens"] + normalized["output_tokens"]
|
|
1353
|
-
)
|
|
2040
|
+
normalized["total_tokens"] = normalized["input_tokens"] + normalized["output_tokens"]
|
|
1354
2041
|
normalized["reported"] = bool(usage) and any(
|
|
1355
2042
|
key in usage
|
|
1356
2043
|
for key in (
|
|
@@ -1391,6 +2078,25 @@ def _apply_plugin_usage(record: RunRecord, usage: Mapping[str, Any]) -> None:
|
|
|
1391
2078
|
)
|
|
1392
2079
|
|
|
1393
2080
|
|
|
2081
|
+
def _is_private_harness_delta(event: RuntimeEvent) -> bool:
|
|
2082
|
+
"""Return whether *event* is an internal token delta, not a UI activity.
|
|
2083
|
+
|
|
2084
|
+
Managed Harness projects its rich event stream to canonical status items.
|
|
2085
|
+
``text.delta`` and ``reasoning.delta`` contain private token fragments,
|
|
2086
|
+
are intentionally ignored by ``run_activity``, and must not be copied into
|
|
2087
|
+
Studio's durable session or run stores. User-visible parent answer
|
|
2088
|
+
streaming uses canonical ``message`` items and therefore remains
|
|
2089
|
+
unaffected.
|
|
2090
|
+
"""
|
|
2091
|
+
|
|
2092
|
+
if not isinstance(event, ItemCompleted) or event.item_kind != "status":
|
|
2093
|
+
return False
|
|
2094
|
+
if event.source.framework != "ksadk":
|
|
2095
|
+
return False
|
|
2096
|
+
native_event_type = str(event.source.metadata.get("native_event_type") or "")
|
|
2097
|
+
return native_event_type in {"reasoning.delta", "text.delta"}
|
|
2098
|
+
|
|
2099
|
+
|
|
1394
2100
|
def project_runtime_event(
|
|
1395
2101
|
event: RuntimeEvent,
|
|
1396
2102
|
*,
|
|
@@ -1520,7 +2226,9 @@ def project_runtime_event(
|
|
|
1520
2226
|
payload = {
|
|
1521
2227
|
"interactionId": event.interaction_id,
|
|
1522
2228
|
"kind": "form",
|
|
1523
|
-
"inputSchema":
|
|
2229
|
+
"inputSchema": event.request.schema_,
|
|
2230
|
+
"message": event.request.prompt or "",
|
|
2231
|
+
"callId": event.source.native_event_id or event.interaction_id,
|
|
1524
2232
|
}
|
|
1525
2233
|
elif isinstance(event, InteractionResolved):
|
|
1526
2234
|
if event.interaction_kind == "approval":
|
|
@@ -1575,9 +2283,7 @@ def project_runtime_event(
|
|
|
1575
2283
|
return projected, payload
|
|
1576
2284
|
|
|
1577
2285
|
|
|
1578
|
-
def _first_content(
|
|
1579
|
-
snapshot: ContentSnapshot | None, content_type: type
|
|
1580
|
-
) -> Any | None:
|
|
2286
|
+
def _first_content(snapshot: ContentSnapshot | None, content_type: type) -> Any | None:
|
|
1581
2287
|
if snapshot is None:
|
|
1582
2288
|
return None
|
|
1583
2289
|
for part in snapshot.parts:
|